mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge pull request #25903 from basepi/merge-forward-develop
Merge forward from 2015.8 to develop
This commit is contained in:
commit
abf389743a
@ -65,7 +65,9 @@
|
||||
# the coloring of the messages, these color formatters also include padding as
|
||||
# well. Color LogRecord attributes are only available for console logging.
|
||||
#
|
||||
#log_fmt_console: '%(colorlevel)s %(colormsg)s'
|
||||
#log_fmt_console: '[%(levelname)-8s] %(message)s'
|
||||
#
|
||||
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
|
||||
|
||||
|
||||
|
@ -696,7 +696,9 @@
|
||||
# the coloring of the messages, these color formatters also include padding as
|
||||
# well. Color LogRecord attributes are only available for console logging.
|
||||
#
|
||||
#log_fmt_console: '%(colorlevel)s %(colormsg)s'
|
||||
#log_fmt_console: '[%(levelname)-8s] %(message)s'
|
||||
#
|
||||
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
|
||||
|
||||
# This can be used to control logging levels more specificically. This
|
||||
|
@ -539,7 +539,9 @@
|
||||
# the coloring of the messages, these color formatters also include padding as
|
||||
# well. Color LogRecord attributes are only available for console logging.
|
||||
#
|
||||
#log_fmt_console: '%(colorlevel)s %(colormsg)s'
|
||||
#log_fmt_console: '[%(levelname)-8s] %(message)s'
|
||||
#
|
||||
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
|
||||
|
||||
# This can be used to control logging levels more specificically. This
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-API" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-API" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-api \- salt-api Command
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-CALL" "1" "June 26, 2015" "2015.5.0-1639-g9f8cef2" "Salt"
|
||||
.TH "SALT-CALL" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-call \- salt-call Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-CLOUD" "1" "June 26, 2015" "2015.5.0-1639-g9f8cef2" "Salt"
|
||||
.TH "SALT-CLOUD" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-cloud \- Salt Cloud Command
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-CP" "1" "June 26, 2015" "2015.5.0-1639-g9f8cef2" "Salt"
|
||||
.TH "SALT-CP" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-cp \- salt-cp Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-KEY" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-KEY" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-key \- salt-key Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-MASTER" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-MASTER" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-master \- salt-master Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-MINION" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-MINION" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-minion \- salt-minion Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-RUN" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-RUN" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-run \- salt-run Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-SSH" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-SSH" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-ssh \- salt-ssh Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-SYNDIC" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-SYNDIC" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-syndic \- salt-syndic Documentation
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-UNITY" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT-UNITY" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt-unity \- salt-unity Command
|
||||
.
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT" "1" "May 06, 2015" "2015.5.0" "Salt"
|
||||
.TH "SALT" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
salt \- salt
|
||||
.
|
||||
|
6625
doc/man/salt.7
6625
doc/man/salt.7
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SPM" "1" "July 07, 2015" "2015.5.0-1703-gf8dc4fc" "Salt"
|
||||
.TH "SPM" "1" "July 29, 2015" "2015.8.0rc2-13-g733b842" "Salt"
|
||||
.SH NAME
|
||||
spm \- Salt Package Manager Command
|
||||
.
|
||||
|
@ -2452,7 +2452,6 @@ Examples:
|
||||
log_file: udp://loghost:10514
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_level
|
||||
|
||||
``log_level``
|
||||
@ -2467,8 +2466,6 @@ The level of messages to send to the console. See also :conf_log:`log_level`.
|
||||
log_level: warning
|
||||
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_level_logfile
|
||||
|
||||
``log_level_logfile``
|
||||
@ -2485,7 +2482,6 @@ it will inherit the level set by :conf_log:`log_level` option.
|
||||
log_level_logfile: warning
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_datefmt
|
||||
|
||||
``log_datefmt``
|
||||
@ -2501,8 +2497,6 @@ The date and time format used in console log messages. See also
|
||||
log_datefmt: '%H:%M:%S'
|
||||
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_datefmt_logfile
|
||||
|
||||
``log_datefmt_logfile``
|
||||
@ -2518,7 +2512,6 @@ The date and time format used in log file messages. See also
|
||||
log_datefmt_logfile: '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_fmt_console
|
||||
|
||||
``log_fmt_console``
|
||||
@ -2529,12 +2522,29 @@ Default: ``[%(levelname)-8s] %(message)s``
|
||||
The format of the console logging messages. See also
|
||||
:conf_log:`log_fmt_console`.
|
||||
|
||||
.. note::
|
||||
Log colors are enabled in ``log_fmt_console`` rather than the
|
||||
:conf_master:`color` config since the logging system is loaded before the
|
||||
master config.
|
||||
|
||||
Console log colors are specified by these additional formatters:
|
||||
|
||||
%(colorlevel)s
|
||||
%(colorname)s
|
||||
%(colorprocess)s
|
||||
%(colormsg)s
|
||||
|
||||
Since it is desirable to include the surrounding brackets, '[' and ']', in
|
||||
the coloring of the messages, these color formatters also include padding
|
||||
as well. Color LogRecord attributes are only available for console
|
||||
logging.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
log_fmt_console: '%(colorlevel)s %(colormsg)s'
|
||||
log_fmt_console: '[%(levelname)-8s] %(message)s'
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_fmt_logfile
|
||||
|
||||
``log_fmt_logfile``
|
||||
@ -2550,7 +2560,6 @@ The format of the log file logging messages. See also
|
||||
log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
|
||||
|
||||
|
||||
|
||||
.. conf_master:: log_granular_levels
|
||||
|
||||
``log_granular_levels``
|
||||
|
@ -145,7 +145,7 @@ the master hostname if name resolution fails. Defaults to 30 seconds.
|
||||
Set to zero if the minion should shutdown and not retry.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
||||
retry_dns: 30
|
||||
|
||||
.. conf_minion:: master_port
|
||||
@ -994,7 +994,6 @@ Examples:
|
||||
log_file: udp://loghost:10514
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_level
|
||||
|
||||
``log_level``
|
||||
@ -1009,8 +1008,6 @@ The level of messages to send to the console. See also :conf_log:`log_level`.
|
||||
log_level: warning
|
||||
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_level_logfile
|
||||
|
||||
``log_level_logfile``
|
||||
@ -1027,7 +1024,6 @@ it will inherit the level set by :conf_log:`log_level` option.
|
||||
log_level_logfile: warning
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_datefmt
|
||||
|
||||
``log_datefmt``
|
||||
@ -1043,8 +1039,6 @@ The date and time format used in console log messages. See also
|
||||
log_datefmt: '%H:%M:%S'
|
||||
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_datefmt_logfile
|
||||
|
||||
``log_datefmt_logfile``
|
||||
@ -1060,7 +1054,6 @@ The date and time format used in log file messages. See also
|
||||
log_datefmt_logfile: '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_fmt_console
|
||||
|
||||
``log_fmt_console``
|
||||
@ -1071,12 +1064,29 @@ Default: ``[%(levelname)-8s] %(message)s``
|
||||
The format of the console logging messages. See also
|
||||
:conf_log:`log_fmt_console`.
|
||||
|
||||
.. note::
|
||||
Log colors are enabled in ``log_fmt_console`` rather than the
|
||||
:conf_minion:`color` config since the logging system is loaded before the
|
||||
minion config.
|
||||
|
||||
Console log colors are specified by these additional formatters:
|
||||
|
||||
%(colorlevel)s
|
||||
%(colorname)s
|
||||
%(colorprocess)s
|
||||
%(colormsg)s
|
||||
|
||||
Since it is desirable to include the surrounding brackets, '[' and ']', in
|
||||
the coloring of the messages, these color formatters also include padding
|
||||
as well. Color LogRecord attributes are only available for console
|
||||
logging.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
log_fmt_console: '%(colorlevel)s %(colormsg)s'
|
||||
log_fmt_console: '[%(levelname)-8s] %(message)s'
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_fmt_logfile
|
||||
|
||||
``log_fmt_logfile``
|
||||
@ -1092,7 +1102,6 @@ The format of the log file logging messages. See also
|
||||
log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: log_granular_levels
|
||||
|
||||
``log_granular_levels``
|
||||
@ -1104,7 +1113,6 @@ This can be used to control logging levels more specifically. See also
|
||||
:conf_log:`log_granular_levels`.
|
||||
|
||||
|
||||
|
||||
.. conf_minion:: failhard
|
||||
|
||||
``failhard``
|
||||
@ -1116,7 +1124,6 @@ Set the global failhard flag, this informs all states to stop running states
|
||||
at the moment a single state fails
|
||||
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
failhard: False
|
||||
|
@ -120,7 +120,8 @@ ssh_key_name
|
||||
ssh_interface
|
||||
This option allows you to create a VM without a public IP. If this option
|
||||
is omitted and the VM does not have a public IP, then the salt-cloud waits
|
||||
for a certain period of time and then destroys the VM.
|
||||
for a certain period of time and then destroys the VM. With the nova drive,
|
||||
private cloud networks can be defined here.
|
||||
|
||||
For more information concerning cloud profiles, see :doc:`here
|
||||
</topics/cloud/profiles>`.
|
||||
@ -144,4 +145,4 @@ cloud-init if available.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
userdata_file: /etc/salt/cloud-init/packages.yml
|
||||
userdata_file: /etc/salt/cloud-init/packages.yml
|
||||
|
@ -86,7 +86,7 @@ Note: You must include the default net-ids when setting networks or the server
|
||||
will be created without the rest of the interfaces
|
||||
|
||||
Note: For rackconnect v3, rackconnectv3 needs to be specified with the
|
||||
rackconnect v3 cloud network as it's variable
|
||||
rackconnect v3 cloud network as its variable.
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
# pylint: disable=E0102
|
||||
@ -333,6 +333,17 @@ def rackconnect(vm_):
|
||||
)
|
||||
|
||||
|
||||
def cloudnetwork(vm_):
|
||||
'''
|
||||
Determine if we should use an extra network to bootstrap
|
||||
Either 'False' (default) or 'True'.
|
||||
'''
|
||||
return config.get_cloud_config_value(
|
||||
'cloudnetwork', vm_, __opts__, default='False',
|
||||
search_global=False
|
||||
)
|
||||
|
||||
|
||||
def managedcloud(vm_):
|
||||
'''
|
||||
Determine if we should wait for the managed cloud automation before
|
||||
@ -628,10 +639,18 @@ def create(vm_):
|
||||
networkname = rackconnectv3
|
||||
for network in node['addresses'].get(networkname, []):
|
||||
if network['version'] is 4:
|
||||
node['extra']['access_ip'] = network['addr']
|
||||
access_ip = network['addr']
|
||||
break
|
||||
vm_['rackconnect'] = True
|
||||
|
||||
if ssh_interface(vm_) in node['addresses']:
|
||||
networkname = ssh_interface(vm_)
|
||||
for network in node['addresses'].get(networkname, []):
|
||||
if network['version'] is 4:
|
||||
node['extra']['access_ip'] = network['addr']
|
||||
break
|
||||
vm_['cloudnetwork'] = True
|
||||
|
||||
if rackconnect(vm_) is True:
|
||||
extra = node.get('extra', {})
|
||||
rc_status = extra.get('metadata', {}).get(
|
||||
@ -689,6 +708,10 @@ def create(vm_):
|
||||
data.public_ips = access_ip
|
||||
return data
|
||||
|
||||
if cloudnetwork(vm_) is True:
|
||||
data.public_ips = access_ip
|
||||
return data
|
||||
|
||||
if result:
|
||||
log.debug('result = {0}'.format(result))
|
||||
data.private_ips = result
|
||||
|
@ -307,6 +307,9 @@ VALID_OPTS = {
|
||||
# Specify the format for state outputs. See highstate outputter for additional details.
|
||||
'state_output': str,
|
||||
|
||||
# Tells the highstate outputter to only report diffs of states that changed
|
||||
'state_output_diff': bool,
|
||||
|
||||
# When true, states run in the order defined in an SLS file, unless requisites re-order them
|
||||
'state_auto_order': bool,
|
||||
|
||||
@ -700,6 +703,7 @@ VALID_OPTS = {
|
||||
# Used by salt-api for master requests timeout
|
||||
'rest_timeout': int,
|
||||
|
||||
# If set, all minion exec module actions will be rerouted through sudo as this user
|
||||
'sudo_user': str,
|
||||
}
|
||||
|
||||
@ -815,6 +819,7 @@ DEFAULT_MINION_OPTS = {
|
||||
'cython_enable': False,
|
||||
'state_verbose': True,
|
||||
'state_output': 'full',
|
||||
'state_output_diff': False,
|
||||
'state_auto_order': True,
|
||||
'state_events': False,
|
||||
'state_aggregate': False,
|
||||
@ -1018,6 +1023,7 @@ DEFAULT_MASTER_OPTS = {
|
||||
'serial': 'msgpack',
|
||||
'state_verbose': True,
|
||||
'state_output': 'full',
|
||||
'state_output_diff': False,
|
||||
'state_auto_order': True,
|
||||
'state_events': False,
|
||||
'state_aggregate': False,
|
||||
|
@ -40,10 +40,23 @@ from __future__ import absolute_import
|
||||
|
||||
# Import third party libs
|
||||
#import salt.ext.six as six
|
||||
|
||||
# Import salt libs
|
||||
from salt.exceptions import (
|
||||
#CommandExecutionError,
|
||||
SaltInvocationError
|
||||
)
|
||||
from salt.utils import warn_until
|
||||
|
||||
from salt.version import (
|
||||
__version__,
|
||||
SaltStackVersion
|
||||
)
|
||||
# is there not SaltStackVersion.current() to get
|
||||
# the version of the salt running this code??
|
||||
CUR_VER = SaltStackVersion(__version__[0], __version__[1])
|
||||
BORON = SaltStackVersion.from_name('Boron')
|
||||
|
||||
# pylint: disable=import-error
|
||||
HAS_GLANCE = False
|
||||
try:
|
||||
@ -77,6 +90,7 @@ def __virtual__():
|
||||
return 'glance'
|
||||
return False
|
||||
|
||||
|
||||
__opts__ = {}
|
||||
|
||||
|
||||
@ -143,12 +157,14 @@ def _auth(profile=None, api_version=2, **connection_args):
|
||||
'get a new one using username and password.')
|
||||
|
||||
if HAS_KEYSTONE:
|
||||
# TODO: redact kwargs['password']
|
||||
log.debug('Calling keystoneclient.v2_0.client.Client(' +
|
||||
'{0}, **{1})'.format(endpoint, kwargs))
|
||||
keystone = kstone.Client(**kwargs)
|
||||
log.debug(help(keystone.get_token))
|
||||
kwargs['token'] = keystone.get_token(keystone.session)
|
||||
# This doesn't realy prevent the password to show up
|
||||
# in the minion log as keystoneclient.session is
|
||||
# logging it anyway when in debug-mode
|
||||
kwargs.pop('password')
|
||||
log.debug('Calling glanceclient.client.Client(' +
|
||||
'{0}, {1}, **{2})'.format(api_version, endpoint, kwargs))
|
||||
@ -158,22 +174,65 @@ def _auth(profile=None, api_version=2, **connection_args):
|
||||
"Can't retrieve a auth_token without keystone")
|
||||
|
||||
|
||||
def image_create(name, location, profile=None, visibility='public',
|
||||
container_format='bare', disk_format='raw'):
|
||||
def _add_image(collection, image):
|
||||
'''
|
||||
Add image to given dictionary
|
||||
'''
|
||||
image_prep = {
|
||||
'id': image.id,
|
||||
'name': image.name,
|
||||
'created_at': image.created_at,
|
||||
'file': image.file,
|
||||
'min_disk': image.min_disk,
|
||||
'min_ram': image.min_ram,
|
||||
'owner': image.owner,
|
||||
'protected': image.protected,
|
||||
'status': image.status,
|
||||
'tags': image.tags,
|
||||
'updated_at': image.updated_at,
|
||||
'visibility': image.visibility,
|
||||
}
|
||||
# Those cause AttributeErrors in Icehouse' glanceclient
|
||||
for attr in ['container_format', 'disk_format', 'size']:
|
||||
if attr in image:
|
||||
image_prep[attr] = image[attr]
|
||||
if type(collection) is dict:
|
||||
collection[image.name] = image_prep
|
||||
elif type(collection) is list:
|
||||
collection.append(image_prep)
|
||||
else:
|
||||
msg = '"collection" is {0}'.format(type(collection)) +\
|
||||
'instead of dict or list.'
|
||||
log.error(msg)
|
||||
raise TypeError(msg)
|
||||
return collection
|
||||
|
||||
|
||||
def image_create(name, location=None, profile=None, visibility=None,
|
||||
container_format='bare', disk_format='raw', protected=None,
|
||||
copy_from=None, is_public=None):
|
||||
'''
|
||||
Create an image (glance image-create)
|
||||
|
||||
CLI Example:
|
||||
CLI Example, old format:
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' glance.image_create name=f16-jeos is_public=true \\
|
||||
disk_format=qcow2 container_format=ovf \\
|
||||
copy_from=http://berrange.fedorapeople.org/\
|
||||
images/2012-02-29/f16-x86_64-openstack-sda.qcow2
|
||||
|
||||
CLI Example, new format resembling Glance API v2:
|
||||
|
||||
salt '*' glance.image_create name=f16-jeos visibility=public \\
|
||||
disk_format=qcow2 container_format=ovf \\
|
||||
copy_from=http://berrange.fedorapeople.org/\
|
||||
images/2012-02-29/f16-x86_64-openstack-sda.qcow2
|
||||
|
||||
For all possible values, run ``glance help image-create`` on the minion.
|
||||
The parameter 'visibility' defaults to 'public' if neither
|
||||
'visibility' nor 'is_public' is specified.
|
||||
'''
|
||||
kwargs = {}
|
||||
# valid options for "visibility":
|
||||
v_list = ['public', 'private']
|
||||
# valid options for "container_format":
|
||||
@ -181,20 +240,52 @@ def image_create(name, location, profile=None, visibility='public',
|
||||
# valid options for "disk_format":
|
||||
df_list = ['ami', 'ari', 'aki', 'vhd', 'vmdk',
|
||||
'raw', 'qcow2', 'vdi', 'iso']
|
||||
if visibility not in v_list:
|
||||
raise SaltInvocationError('"visibility" needs to be one ' +
|
||||
'of the following: {0}'.format(', '.join(v_list)))
|
||||
# 'location' and 'visibility' are the parameters used in
|
||||
# Glance API v2. For now we have to use v1 for now (see below)
|
||||
# but this modules interface will change in Carbon.
|
||||
if copy_from is not None or is_public is not None:
|
||||
warn_until('Carbon', 'The parameters \'copy_from\' and '
|
||||
'\'is_public\' are deprecated and will be removed. '
|
||||
'Use \'location\' and \'visibility\' instead.')
|
||||
if is_public is not None and visibility is not None:
|
||||
raise SaltInvocationError('Must only specify one of '
|
||||
'\'is_public\' and \'visibility\'')
|
||||
if copy_from is not None and location is not None:
|
||||
raise SaltInvocationError('Must only specify one of '
|
||||
'\'copy_from\' and \'location\'')
|
||||
if copy_from is not None:
|
||||
kwargs['copy_from'] = copy_from
|
||||
else:
|
||||
kwargs['copy_from'] = location
|
||||
if is_public is not None:
|
||||
kwargs['is_public'] = is_public
|
||||
elif visibility is not None:
|
||||
if visibility not in v_list:
|
||||
raise SaltInvocationError('"visibility" needs to be one ' +
|
||||
'of the following: {0}'.format(', '.join(v_list)))
|
||||
elif visibility == 'public':
|
||||
kwargs['is_public'] = True
|
||||
else:
|
||||
kwargs['is_public'] = False
|
||||
else:
|
||||
kwargs['is_public'] = True
|
||||
if container_format not in cf_list:
|
||||
raise SaltInvocationError('"container_format" needs to be ' +
|
||||
'one of the following: {0}'.format(', '.join(cf_list)))
|
||||
else:
|
||||
kwargs['container_format'] = container_format
|
||||
if disk_format not in df_list:
|
||||
raise SaltInvocationError('"disk_format" needs to be one ' +
|
||||
'of the following: {0}'.format(', '.join(df_list)))
|
||||
else:
|
||||
kwargs['disk_format'] = disk_format
|
||||
if protected is not None:
|
||||
kwargs['protected'] = protected
|
||||
# Icehouse's glanceclient doesn't have add_location() and
|
||||
# glanceclient.v2 doesn't implement Client.images.create()
|
||||
# in a usable fashion. Thus we have to use v1 for now.
|
||||
g_client = _auth(profile, api_version=1)
|
||||
image = g_client.images.create(name=name, copy_from=location)
|
||||
image = g_client.images.create(name=name, **kwargs)
|
||||
return image_show(image.id)
|
||||
|
||||
|
||||
@ -217,8 +308,15 @@ def image_delete(id=None, name=None, profile=None): # pylint: disable=C0103
|
||||
id = image.id # pylint: disable=C0103
|
||||
continue
|
||||
if not id:
|
||||
return {'Error': 'Unable to resolve image id'}
|
||||
g_client.images.delete(id)
|
||||
return {'Error': 'Unable to resolve '
|
||||
'image id for name {0}'.format(name)}
|
||||
try:
|
||||
g_client.images.delete(id)
|
||||
except exc.HTTPNotFound:
|
||||
return {'Error': 'No image with ID {0}'.format(id)}
|
||||
except exc.HTTPForbidden as forbidden:
|
||||
log.error(str(forbidden))
|
||||
return {'Error': str(forbidden)}
|
||||
ret = 'Deleted image with ID {0}'.format(id)
|
||||
if name:
|
||||
ret += ' ({0})'.format(name)
|
||||
@ -248,18 +346,27 @@ def image_show(id=None, name=None, profile=None): # pylint: disable=C0103
|
||||
pformat = pprint.PrettyPrinter(indent=4).pformat
|
||||
log.debug('Properties of image {0}:\n{1}'.format(
|
||||
image.name, pformat(image)))
|
||||
# TODO: Get rid of the wrapping dict, see #24568
|
||||
ret[image.name] = {}
|
||||
ret_details = {}
|
||||
# I may want to use this code on Beryllium
|
||||
# until we got Boron packages for Ubuntu
|
||||
# so please keep this code until Carbon!
|
||||
warn_until('Carbon', 'Starting with \'Boron\' image_show() '
|
||||
'will stop wrapping the returned image in another '
|
||||
'dictionary.')
|
||||
if CUR_VER < BORON:
|
||||
ret[image.name] = ret_details
|
||||
else:
|
||||
ret = ret_details
|
||||
schema = image_schema(profile=profile)
|
||||
if len(schema.keys()) == 1:
|
||||
schema = schema['image']
|
||||
for key in schema.keys():
|
||||
if key in image:
|
||||
ret[image.name][key] = image[key]
|
||||
ret_details[key] = image[key]
|
||||
return ret
|
||||
|
||||
|
||||
def image_list(id=None, profile=None): # pylint: disable=C0103
|
||||
def image_list(id=None, profile=None, name=None): # pylint: disable=C0103
|
||||
'''
|
||||
Return a list of available images (glance image-list)
|
||||
|
||||
@ -270,29 +377,30 @@ def image_list(id=None, profile=None): # pylint: disable=C0103
|
||||
salt '*' glance.image_list
|
||||
'''
|
||||
g_client = _auth(profile)
|
||||
ret = {}
|
||||
# TODO: Get rid of the wrapping dict, see #24568
|
||||
# I may want to use this code on Beryllium
|
||||
# until we got Boron packages for Ubuntu
|
||||
# so please keep this code until Carbon!
|
||||
warn_until('Carbon', 'Starting in \'Boron\' image_list() '
|
||||
'will return a list of images instead of a dictionary '
|
||||
'keyed with the images\' names.')
|
||||
if CUR_VER < BORON:
|
||||
ret = {}
|
||||
else:
|
||||
ret = []
|
||||
for image in g_client.images.list():
|
||||
ret[image.name] = {
|
||||
'id': image.id,
|
||||
'name': image.name,
|
||||
'created_at': image.created_at,
|
||||
'file': image.file,
|
||||
'min_disk': image.min_disk,
|
||||
'min_ram': image.min_ram,
|
||||
'owner': image.owner,
|
||||
'protected': image.protected,
|
||||
'status': image.status,
|
||||
'tags': image.tags,
|
||||
'updated_at': image.updated_at,
|
||||
'visibility': image.visibility,
|
||||
}
|
||||
# Those cause AttributeErrors in Icehouse' glanceclient
|
||||
for attr in ['container_format', 'disk_format', 'size']:
|
||||
if attr in image:
|
||||
ret[image.name][attr] = image[attr]
|
||||
if id == image.id:
|
||||
return ret[image.name]
|
||||
if id is None and name is None:
|
||||
_add_image(ret, image)
|
||||
else:
|
||||
if id is not None and id == image.id:
|
||||
_add_image(ret, image)
|
||||
return ret
|
||||
if name == image.name:
|
||||
if name in ret and CUR_VER < BORON:
|
||||
# Not really worth an exception
|
||||
return {'Error': 'More than one image '
|
||||
'with name "{0}"'.format(name)}
|
||||
_add_image(ret, image)
|
||||
log.debug('Returning images: {0}'.format(ret))
|
||||
return ret
|
||||
|
||||
|
||||
@ -304,6 +412,52 @@ def image_schema(profile=None):
|
||||
return schema_get('image', profile)
|
||||
|
||||
|
||||
def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable=C0103
|
||||
'''
|
||||
Update properties of given image.
|
||||
Known to work for:
|
||||
- min_ram (in MB)
|
||||
- protected (bool)
|
||||
- visibility ('public' or 'private')
|
||||
'''
|
||||
if id:
|
||||
image = image_show(id=id)
|
||||
# TODO: This unwrapping should get a warn_until
|
||||
if len(image) == 1:
|
||||
image = image.values()[0]
|
||||
elif name:
|
||||
img_list = image_list(name=name)
|
||||
if img_list is not list and 'Error' in img_list:
|
||||
return img_list
|
||||
elif len(img_list) == 0:
|
||||
return {'result': False,
|
||||
'comment': 'No image with name \'{0}\' '
|
||||
'found.'.format(name)}
|
||||
elif len(img_list) == 1:
|
||||
image = img_list[0]
|
||||
else:
|
||||
raise SaltInvocationError
|
||||
log.debug('Found image:\n{0}'.format(image))
|
||||
to_update = {}
|
||||
for key, value in kwargs.items():
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
if key not in image or image[key] != value:
|
||||
log.debug('add <{0}={1}> to to_update'.format(key, value))
|
||||
to_update[key] = value
|
||||
g_client = _auth(profile)
|
||||
updated = g_client.images.update(image['id'], **to_update)
|
||||
# I may want to use this code on Beryllium
|
||||
# until we got Boron packages for Ubuntu
|
||||
# so please keep this code until Carbon!
|
||||
warn_until('Carbon', 'Starting with \'Boron\' image_update() '
|
||||
'will stop wrapping the returned, updated image in '
|
||||
'another dictionary.')
|
||||
if CUR_VER < BORON:
|
||||
updated = {updated.name: updated}
|
||||
return updated
|
||||
|
||||
|
||||
def schema_get(name, profile=None):
|
||||
'''
|
||||
Known valid names of schemas are:
|
||||
@ -343,11 +497,10 @@ def _item_list(profile=None):
|
||||
return ret
|
||||
|
||||
|
||||
#The following is a list of functions that need to be incorporated in the
|
||||
#glance module. This list should be updated as functions are added.
|
||||
# The following is a list of functions that need to be incorporated in the
|
||||
# glance module. This list should be updated as functions are added.
|
||||
|
||||
# image-download Download a specific image.
|
||||
# image-update Update a specific image.
|
||||
# member-create Share a specific image with a tenant.
|
||||
# member-delete Remove a shared image from a tenant.
|
||||
# member-list Describe sharing permissions by image or tenant.
|
||||
|
@ -218,7 +218,7 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None,
|
||||
|
||||
if 'connstate' in kwargs:
|
||||
if '-m state' not in rule:
|
||||
rule += '-m state '
|
||||
rule.append('-m state')
|
||||
|
||||
rule.append('{0}--state {1}'.format(maybe_add_negation('connstate'), kwargs['connstate']))
|
||||
|
||||
|
@ -376,7 +376,7 @@ def _format_host(host, data):
|
||||
sum_duration /= 1000
|
||||
duration_unit = 's'
|
||||
total_duration = u'Total run time: {0} {1}'.format(
|
||||
'{:.3f}'.format(sum_duration).rjust(line_max_len - 5),
|
||||
'{0:.3f}'.format(sum_duration).rjust(line_max_len - 5),
|
||||
duration_unit)
|
||||
hstrs.append(colorfmt.format(colors['CYAN'], total_duration, colors))
|
||||
|
||||
|
@ -144,6 +144,8 @@ def up(): # pylint: disable=C0103
|
||||
|
||||
def list_state(subset=None, show_ipv4=False, state=None):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -157,8 +159,6 @@ def list_state(subset=None, show_ipv4=False, state=None):
|
||||
Show minions being in specific state that is one of 'available', 'joined',
|
||||
'allowed', 'alived' or 'reaped'.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -190,6 +190,8 @@ def list_state(subset=None, show_ipv4=False, state=None):
|
||||
|
||||
def list_not_state(subset=None, show_ipv4=False, state=None):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -203,8 +205,6 @@ def list_not_state(subset=None, show_ipv4=False, state=None):
|
||||
Show minions being in specific state that is one of 'available', 'joined',
|
||||
'allowed', 'alived' or 'reaped'.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -236,16 +236,12 @@ def present(subset=None, show_ipv4=False):
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
subset : None
|
||||
Pass in a CIDR range to filter minions by IP address.
|
||||
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -257,6 +253,8 @@ def present(subset=None, show_ipv4=False):
|
||||
|
||||
def not_present(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.5.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent)
|
||||
|
||||
@ -266,8 +264,6 @@ def not_present(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -279,6 +275,8 @@ def not_present(subset=None, show_ipv4=False):
|
||||
|
||||
def joined(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -288,8 +286,6 @@ def joined(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -301,6 +297,8 @@ def joined(subset=None, show_ipv4=False):
|
||||
|
||||
def not_joined(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent)
|
||||
|
||||
@ -310,8 +308,6 @@ def not_joined(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -323,6 +319,8 @@ def not_joined(subset=None, show_ipv4=False):
|
||||
|
||||
def allowed(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -332,8 +330,6 @@ def allowed(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -345,6 +341,8 @@ def allowed(subset=None, show_ipv4=False):
|
||||
|
||||
def not_allowed(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent)
|
||||
|
||||
@ -354,8 +352,6 @@ def not_allowed(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -367,6 +363,8 @@ def not_allowed(subset=None, show_ipv4=False):
|
||||
|
||||
def alived(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -376,8 +374,6 @@ def alived(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -389,6 +385,8 @@ def alived(subset=None, show_ipv4=False):
|
||||
|
||||
def not_alived(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent)
|
||||
|
||||
@ -398,8 +396,6 @@ def not_alived(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -411,6 +407,8 @@ def not_alived(subset=None, show_ipv4=False):
|
||||
|
||||
def reaped(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are up according to Salt's presence
|
||||
detection (no commands will be sent to minions)
|
||||
|
||||
@ -420,8 +418,6 @@ def reaped(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -433,6 +429,8 @@ def reaped(subset=None, show_ipv4=False):
|
||||
|
||||
def not_reaped(subset=None, show_ipv4=False):
|
||||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Print a list of all minions that are NOT up according to Salt's presence
|
||||
detection (no commands will be sent)
|
||||
|
||||
@ -442,8 +440,6 @@ def not_reaped(subset=None, show_ipv4=False):
|
||||
show_ipv4 : False
|
||||
Also show the IP address each minion is connecting from.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
250
salt/states/glance.py
Normal file
250
salt/states/glance.py
Normal file
@ -0,0 +1,250 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Managing Images in OpenStack Glance
|
||||
===================================
|
||||
'''
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import time
|
||||
|
||||
# Import salt libs
|
||||
from salt.utils import warn_until
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _find_image(name):
|
||||
'''
|
||||
Tries to find image with given name, returns
|
||||
- image, 'Found image <name>'
|
||||
- None, 'No such image found'
|
||||
- False, 'Found more than one image with given name'
|
||||
'''
|
||||
images_dict = __salt__['glance.image_list'](name=name)
|
||||
log.debug('Got images_dict: {0}'.format(images_dict))
|
||||
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'glance.image_list\' is not supposed to return '
|
||||
'the images wrapped in a separate dict anymore.')
|
||||
if len(images_dict) == 1 and 'images' in images_dict:
|
||||
images_dict = images_dict['images']
|
||||
|
||||
# I /think/ this will still work when glance.image_list
|
||||
# starts returning a list instead of a dictionary...
|
||||
if len(images_dict) == 0:
|
||||
return None, 'No image with name "{0}"'.format(name)
|
||||
elif len(images_dict) == 1:
|
||||
return images_dict.values()[0], 'Found image {0}'.format(name)
|
||||
elif len(images_dict) > 1:
|
||||
return False, 'Found more than one image with given name'
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def image_present(name, visibility='public', protected=None,
|
||||
checksum=None, location=None, wait_for=None, timeout=30):
|
||||
'''
|
||||
Checks if given image is present with properties
|
||||
set as specified.
|
||||
|
||||
An image should got through the stages 'queued', 'saving'
|
||||
before becoming 'active'. The attribute 'checksum' can
|
||||
only be checked once the image is active.
|
||||
If you don't specify 'wait_for' but 'checksum' the function
|
||||
will wait for the image to become active before comparing
|
||||
checksums. If you don't specify checksum either the function
|
||||
will return when the image reached 'saving'.
|
||||
The default timeout for both is 30 seconds.
|
||||
|
||||
Supported properties:
|
||||
- visibility ('public' or 'private')
|
||||
- protected (bool)
|
||||
- checksum (string, md5sum)
|
||||
- location (URL, to copy from)
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': '',
|
||||
}
|
||||
acceptable = ['queued', 'saving', 'active']
|
||||
if wait_for is None and checksum is None:
|
||||
wait_for = 'saving'
|
||||
elif wait_for is None and checksum is not None:
|
||||
wait_for = 'active'
|
||||
|
||||
# Just pop states until we reach the
|
||||
# first acceptable one:
|
||||
while len(acceptable) > 1:
|
||||
if acceptable[0] == wait_for:
|
||||
break
|
||||
else:
|
||||
acceptable.pop(0)
|
||||
|
||||
image, msg = _find_image(name)
|
||||
if image is False:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
log.debug(msg)
|
||||
# No image yet and we know where to get one
|
||||
if image is None and location is not None:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'glance.image_present would ' \
|
||||
'create an image from {0}'.format(location)
|
||||
return ret
|
||||
image = __salt__['glance.image_create'](name=name,
|
||||
protected=protected, visibility=visibility,
|
||||
location=location)
|
||||
# See Salt issue #24568
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'glance.image_create\' is not supposed to return '
|
||||
'the image wrapped in a dict anymore.')
|
||||
if len(image.keys()) == 1:
|
||||
image = image.values()[0]
|
||||
log.debug('Created new image:\n{0}'.format(image))
|
||||
ret['changes'] = {
|
||||
name:
|
||||
{
|
||||
'new':
|
||||
{
|
||||
'id': image['id']
|
||||
},
|
||||
'old': None
|
||||
}
|
||||
}
|
||||
timer = timeout
|
||||
# Kinda busy-loopy but I don't think the Glance
|
||||
# API has events we can listen for
|
||||
while timer > 0:
|
||||
if 'status' in image and \
|
||||
image['status'] in acceptable:
|
||||
log.debug('Image {0} has reached status {1}'.format(
|
||||
image['name'], image['status']))
|
||||
break
|
||||
else:
|
||||
timer -= 5
|
||||
time.sleep(5)
|
||||
image, msg = _find_image(name)
|
||||
if not image:
|
||||
ret['result'] = False
|
||||
ret['comment'] += 'Created image {0} '.format(
|
||||
name) + ' vanished:\n' + msg
|
||||
return ret
|
||||
elif len(image.keys()) == 1:
|
||||
# See Salt issue #24568
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'_find_image()\' is not supposed to return '
|
||||
'the image wrapped in a dict anymore.')
|
||||
image = image.values()[0]
|
||||
if timer <= 0 and image['status'] not in acceptable:
|
||||
ret['result'] = False
|
||||
ret['comment'] += 'Image didn\'t reach an acceptable '+\
|
||||
'state ({0}) before timeout:\n'.format(acceptable)+\
|
||||
'\tLast status was "{0}".\n'.format(image['status'])
|
||||
|
||||
# See Salt issue #24568
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'_find_image()\' is not supposed to return '
|
||||
'the image wrapped in a dict anymore.')
|
||||
if len(image.keys()) == 1:
|
||||
image = image.values()[0]
|
||||
# ret[comment] +=
|
||||
|
||||
# There's no image but where would I get one??
|
||||
elif location is None:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'No location to copy image from specified,\n' +\
|
||||
'glance.image_present would not create one'
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'No location to copy image from specified,\n' +\
|
||||
'not creating a new image.'
|
||||
return ret
|
||||
|
||||
# If we've created a new image also return its last status:
|
||||
if name in ret['changes']:
|
||||
ret['changes'][name]['new']['status'] = image['status']
|
||||
|
||||
if visibility:
|
||||
if image['visibility'] != visibility:
|
||||
old_value = image['visibility']
|
||||
if not __opts__['test']:
|
||||
image = __salt__['glance.image_update'](
|
||||
id=image['id'], visibility=visibility)
|
||||
# See Salt issue #24568
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'glance.image_update\' is not supposed to return '
|
||||
'the image wrapped in a dict anymore.')
|
||||
if len(image.keys()) == 1:
|
||||
image = image.values()[0]
|
||||
# Check if image_update() worked:
|
||||
if image['visibility'] != visibility:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
elif __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] += '"visibility" is {0}, '\
|
||||
'should be {1}.\n'.format(image['visibility'],
|
||||
visibility)
|
||||
else:
|
||||
if 'new' in ret['changes']:
|
||||
ret['changes']['new']['visibility'] = visibility
|
||||
else:
|
||||
ret['changes']['new'] = {'visibility': visibility}
|
||||
if 'old' in ret['changes']:
|
||||
ret['changes']['old']['visibility'] = old_value
|
||||
else:
|
||||
ret['changes']['old'] = {'visibility': old_value}
|
||||
else:
|
||||
ret['comment'] += '"visibility" is correct ({0}).\n'.format(
|
||||
visibility)
|
||||
if protected is not None:
|
||||
if not isinstance(protected, bool) or image['protected'] ^ protected:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
else:
|
||||
ret['result'] = None
|
||||
ret['comment'] += '"protected" is {0}, should be {1}.\n'.format(
|
||||
image['protected'], protected)
|
||||
else:
|
||||
ret['comment'] += '"protected" is correct ({0}).\n'.format(
|
||||
protected)
|
||||
if 'status' in image and checksum:
|
||||
if image['status'] == 'active':
|
||||
if 'checksum' not in image:
|
||||
# Refresh our info about the image
|
||||
image = __salt__['glance.image_show'](image['id'])
|
||||
warn_until('Boron', 'Starting with Boron '
|
||||
'\'glance.image_show\' is not supposed to return '
|
||||
'the image wrapped in a dict anymore.')
|
||||
if len(image.keys()) == 1:
|
||||
image = image.values()[0]
|
||||
if 'checksum' not in image:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
else:
|
||||
ret['result'] = None
|
||||
ret['comment'] += 'No checksum available for this image:\n' +\
|
||||
'\tImage has status "{0}".'.format(image['status'])
|
||||
elif image['checksum'] != checksum:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
else:
|
||||
ret['result'] = None
|
||||
ret['comment'] += '"checksum" is {0}, should be {1}.\n'.format(
|
||||
image['checksum'], checksum)
|
||||
else:
|
||||
ret['comment'] += '"checksum" is correct ({0}).\n'.format(
|
||||
checksum)
|
||||
elif image['status'] in ['saving', 'queued']:
|
||||
ret['comment'] += 'Checksum won\'t be verified as image ' +\
|
||||
'hasn\'t reached\n\t "status=active" yet.\n'
|
||||
log.debug('glance.image_present will return: {0}'.format(ret))
|
||||
return ret
|
@ -1586,6 +1586,40 @@ def is_sunos():
|
||||
return sys.platform.startswith('sunos')
|
||||
|
||||
|
||||
@real_memoize
|
||||
def is_smartos():
|
||||
'''
|
||||
Simple function to return if host is SmartOS (Illumos) or not
|
||||
'''
|
||||
if not is_sunos():
|
||||
return False
|
||||
else:
|
||||
return os.uname()[3].startswith('joyent_')
|
||||
|
||||
|
||||
@real_memoize
|
||||
def is_smartos_globalzone():
|
||||
'''
|
||||
Function to return if host is SmartOS (Illumos) global zone or not
|
||||
'''
|
||||
if not is_smartos():
|
||||
return False
|
||||
else:
|
||||
cmd = ['zonename']
|
||||
try:
|
||||
zonename = subprocess.Popen(
|
||||
cmd, shell=False,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except OSError:
|
||||
return False
|
||||
if zonename.returncode:
|
||||
return False
|
||||
if zonename.stdout.read().strip() == 'global':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@real_memoize
|
||||
def is_freebsd():
|
||||
'''
|
||||
|
@ -7,11 +7,14 @@ from __future__ import absolute_import
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.concurrent
|
||||
LOOP_CLASS = tornado.ioloop.IOLoop
|
||||
# attempt to use zmq-- if we have it otherwise fallback to tornado loop
|
||||
try:
|
||||
import zmq.eventloop.ioloop
|
||||
# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x
|
||||
if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'):
|
||||
zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop
|
||||
LOOP_CLASS = zmq.eventloop.ioloop.ZMQIOLoop
|
||||
except ImportError:
|
||||
pass # salt-ssh doesn't dep zmq
|
||||
|
||||
@ -47,6 +50,8 @@ class SyncWrapper(object):
|
||||
ret = sync.async_method()
|
||||
'''
|
||||
loop_map = weakref.WeakKeyDictionary() # keep a mapping of parent io_loop -> sync_loop
|
||||
# Can't use WeakSet since we have to support python 2.6 :(
|
||||
loops_in_use = weakref.WeakKeyDictionary() # set of sync_loops in use
|
||||
|
||||
def __init__(self, method, args=tuple(), kwargs=None):
|
||||
if kwargs is None:
|
||||
@ -54,14 +59,25 @@ class SyncWrapper(object):
|
||||
|
||||
parent_io_loop = tornado.ioloop.IOLoop.current()
|
||||
if parent_io_loop not in SyncWrapper.loop_map:
|
||||
SyncWrapper.loop_map[parent_io_loop] = zmq.eventloop.ioloop.ZMQIOLoop()
|
||||
SyncWrapper.loop_map[parent_io_loop] = LOOP_CLASS()
|
||||
|
||||
self.io_loop = SyncWrapper.loop_map[parent_io_loop]
|
||||
io_loop = SyncWrapper.loop_map[parent_io_loop]
|
||||
if io_loop in self.loops_in_use:
|
||||
io_loop = LOOP_CLASS()
|
||||
self.io_loop = io_loop
|
||||
self.loops_in_use[self.io_loop] = True
|
||||
kwargs['io_loop'] = self.io_loop
|
||||
|
||||
with current_ioloop(self.io_loop):
|
||||
self.async = method(*args, **kwargs)
|
||||
|
||||
def __del__(self):
|
||||
'''
|
||||
Once the async wrapper is complete, remove our loop from the in use set
|
||||
so someone else can use it without making another one
|
||||
'''
|
||||
del self.loops_in_use[self.io_loop]
|
||||
|
||||
def __getattribute__(self, key):
|
||||
try:
|
||||
return object.__getattribute__(self, key)
|
||||
|
@ -2710,8 +2710,9 @@ class SPMParser(six.with_metaclass(OptionParserMeta,
|
||||
def _mixin_after_parsed(self):
|
||||
# spm needs arguments
|
||||
if len(self.args) <= 1:
|
||||
self.print_help()
|
||||
self.exit(salt.defaults.exitcodes.EX_USAGE)
|
||||
if self.args[0] not in ('update_repo',):
|
||||
self.print_help()
|
||||
self.exit(salt.defaults.exitcodes.EX_USAGE)
|
||||
|
||||
def setup_config(self):
|
||||
return salt.config.spm_config(self.get_config_file_path())
|
||||
|
@ -5,6 +5,9 @@ Create and verify ANSI X9.31 RSA signatures using OpenSSL libcrypto
|
||||
|
||||
# python libs
|
||||
from __future__ import absolute_import
|
||||
import glob
|
||||
import sys
|
||||
import os
|
||||
|
||||
# salt libs
|
||||
import salt.utils
|
||||
@ -12,7 +15,6 @@ import salt.utils
|
||||
# 3rd-party libs
|
||||
from ctypes import cdll, c_char_p, c_int, c_void_p, pointer, create_string_buffer
|
||||
from ctypes.util import find_library
|
||||
import sys
|
||||
|
||||
|
||||
def _load_libcrypto():
|
||||
@ -21,6 +23,10 @@ def _load_libcrypto():
|
||||
'''
|
||||
if sys.platform.startswith('win'):
|
||||
return cdll.LoadLibrary('libeay32')
|
||||
elif getattr(sys, 'frozen', False) and salt.utils.is_smartos():
|
||||
return cdll.LoadLibrary(glob.glob(os.path.join(
|
||||
os.path.dirname(sys.executable),
|
||||
'libcrypto.so*'))[0])
|
||||
else:
|
||||
lib = find_library('crypto')
|
||||
if lib:
|
||||
|
78
tests/unit/utils/async_test.py
Normal file
78
tests/unit/utils/async_test.py
Normal file
@ -0,0 +1,78 @@
|
||||
# coding: utf-8
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import 3rd-party libs
|
||||
import tornado.testing
|
||||
import tornado.gen
|
||||
from tornado.testing import AsyncTestCase
|
||||
|
||||
from salt.utils import async
|
||||
|
||||
|
||||
class HelperA(object):
|
||||
def __init__(self, io_loop=None):
|
||||
pass
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def sleep(self):
|
||||
yield tornado.gen.sleep(0.5)
|
||||
raise tornado.gen.Return(True)
|
||||
|
||||
|
||||
class HelperB(object):
|
||||
def __init__(self, a=None, io_loop=None):
|
||||
if a is None:
|
||||
a = async.SyncWrapper(HelperA)
|
||||
self.a = a
|
||||
|
||||
@tornado.gen.coroutine
|
||||
def sleep(self):
|
||||
yield tornado.gen.sleep(0.5)
|
||||
self.a.sleep()
|
||||
raise tornado.gen.Return(False)
|
||||
|
||||
|
||||
class TestSyncWrapper(AsyncTestCase):
|
||||
@tornado.testing.gen_test
|
||||
def test_helpers(self):
|
||||
'''
|
||||
Test that the helper classes do what we expect within a regular async env
|
||||
'''
|
||||
ha = HelperA()
|
||||
ret = yield ha.sleep()
|
||||
self.assertTrue(ret)
|
||||
|
||||
hb = HelperB()
|
||||
ret = yield hb.sleep()
|
||||
self.assertFalse(ret)
|
||||
|
||||
def test_basic_wrap(self):
|
||||
'''
|
||||
Test that we can wrap an async caller.
|
||||
'''
|
||||
sync = async.SyncWrapper(HelperA)
|
||||
ret = sync.sleep()
|
||||
self.assertTrue(ret)
|
||||
|
||||
def test_double(self):
|
||||
'''
|
||||
Test when the async wrapper object itself creates a wrap of another thing
|
||||
|
||||
This works fine since the second wrap is based on the first's IOLoop so we
|
||||
don't have to worry about complex start/stop mechanics
|
||||
'''
|
||||
sync = async.SyncWrapper(HelperB)
|
||||
ret = sync.sleep()
|
||||
self.assertFalse(ret)
|
||||
|
||||
def test_double_sameloop(self):
|
||||
'''
|
||||
Test async wrappers initiated from the same IOLoop, to ensure that
|
||||
we don't wire up both to the same IOLoop (since it causes MANY problems).
|
||||
'''
|
||||
a = async.SyncWrapper(HelperA)
|
||||
sync = async.SyncWrapper(HelperB, (a,))
|
||||
ret = sync.sleep()
|
||||
self.assertFalse(ret)
|
Loading…
Reference in New Issue
Block a user