Merge pull request #25903 from basepi/merge-forward-develop

Merge forward from 2015.8 to develop
This commit is contained in:
Colton Myers 2015-08-03 11:12:18 -06:00
commit abf389743a
32 changed files with 5781 additions and 1652 deletions

View File

@ -65,7 +65,9 @@
# the coloring of the messages, these color formatters also include padding as # the coloring of the messages, these color formatters also include padding as
# well. Color LogRecord attributes are only available for console logging. # 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_console: '[%(levelname)-8s] %(message)s'
#
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s' #log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'

View File

@ -696,7 +696,9 @@
# the coloring of the messages, these color formatters also include padding as # the coloring of the messages, these color formatters also include padding as
# well. Color LogRecord attributes are only available for console logging. # 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_console: '[%(levelname)-8s] %(message)s'
#
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(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 # This can be used to control logging levels more specificically. This

View File

@ -539,7 +539,9 @@
# the coloring of the messages, these color formatters also include padding as # the coloring of the messages, these color formatters also include padding as
# well. Color LogRecord attributes are only available for console logging. # 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_console: '[%(levelname)-8s] %(message)s'
#
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(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 # This can be used to control logging levels more specificically. This

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-api \- salt-api Command salt-api \- salt-api Command
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-call \- salt-call Documentation salt-call \- salt-call Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-cloud \- Salt Cloud Command salt-cloud \- Salt Cloud Command
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-cp \- salt-cp Documentation salt-cp \- salt-cp Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-key \- salt-key Documentation salt-key \- salt-key Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-master \- salt-master Documentation salt-master \- salt-master Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-minion \- salt-minion Documentation salt-minion \- salt-minion Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-run \- salt-run Documentation salt-run \- salt-run Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-ssh \- salt-ssh Documentation salt-ssh \- salt-ssh Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-syndic \- salt-syndic Documentation salt-syndic \- salt-syndic Documentation
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt-unity \- salt-unity Command salt-unity \- salt-unity Command
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
salt \- salt salt \- salt
. .

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" 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 .SH NAME
spm \- Salt Package Manager Command spm \- Salt Package Manager Command
. .

View File

@ -2452,7 +2452,6 @@ Examples:
log_file: udp://loghost:10514 log_file: udp://loghost:10514
.. conf_master:: log_level .. conf_master:: log_level
``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 log_level: warning
.. conf_master:: log_level_logfile .. conf_master:: log_level_logfile
``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 log_level_logfile: warning
.. conf_master:: log_datefmt .. conf_master:: log_datefmt
``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' log_datefmt: '%H:%M:%S'
.. conf_master:: log_datefmt_logfile .. conf_master:: log_datefmt_logfile
``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' log_datefmt_logfile: '%Y-%m-%d %H:%M:%S'
.. conf_master:: log_fmt_console .. conf_master:: log_fmt_console
``log_fmt_console`` ``log_fmt_console``
@ -2529,12 +2522,29 @@ Default: ``[%(levelname)-8s] %(message)s``
The format of the console logging messages. See also The format of the console logging messages. See also
:conf_log:`log_fmt_console`. :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 .. code-block:: yaml
log_fmt_console: '%(colorlevel)s %(colormsg)s'
log_fmt_console: '[%(levelname)-8s] %(message)s' log_fmt_console: '[%(levelname)-8s] %(message)s'
.. conf_master:: log_fmt_logfile .. conf_master:: log_fmt_logfile
``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' log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
.. conf_master:: log_granular_levels .. conf_master:: log_granular_levels
``log_granular_levels`` ``log_granular_levels``

View File

@ -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. Set to zero if the minion should shutdown and not retry.
.. code-block:: yaml .. code-block:: yaml
retry_dns: 30 retry_dns: 30
.. conf_minion:: master_port .. conf_minion:: master_port
@ -994,7 +994,6 @@ Examples:
log_file: udp://loghost:10514 log_file: udp://loghost:10514
.. conf_minion:: log_level .. conf_minion:: log_level
``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 log_level: warning
.. conf_minion:: log_level_logfile .. conf_minion:: log_level_logfile
``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 log_level_logfile: warning
.. conf_minion:: log_datefmt .. conf_minion:: log_datefmt
``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' log_datefmt: '%H:%M:%S'
.. conf_minion:: log_datefmt_logfile .. conf_minion:: log_datefmt_logfile
``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' log_datefmt_logfile: '%Y-%m-%d %H:%M:%S'
.. conf_minion:: log_fmt_console .. conf_minion:: log_fmt_console
``log_fmt_console`` ``log_fmt_console``
@ -1071,12 +1064,29 @@ Default: ``[%(levelname)-8s] %(message)s``
The format of the console logging messages. See also The format of the console logging messages. See also
:conf_log:`log_fmt_console`. :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 .. code-block:: yaml
log_fmt_console: '%(colorlevel)s %(colormsg)s'
log_fmt_console: '[%(levelname)-8s] %(message)s' log_fmt_console: '[%(levelname)-8s] %(message)s'
.. conf_minion:: log_fmt_logfile .. conf_minion:: log_fmt_logfile
``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' log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
.. conf_minion:: log_granular_levels .. conf_minion:: log_granular_levels
``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_log:`log_granular_levels`.
.. conf_minion:: failhard .. conf_minion:: failhard
``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 at the moment a single state fails
.. code-block:: yaml .. code-block:: yaml
failhard: False failhard: False

View File

@ -120,7 +120,8 @@ ssh_key_name
ssh_interface ssh_interface
This option allows you to create a VM without a public IP. If this option 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 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 For more information concerning cloud profiles, see :doc:`here
</topics/cloud/profiles>`. </topics/cloud/profiles>`.
@ -144,4 +145,4 @@ cloud-init if available.
.. code-block:: yaml .. code-block:: yaml
userdata_file: /etc/salt/cloud-init/packages.yml userdata_file: /etc/salt/cloud-init/packages.yml

View File

@ -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 will be created without the rest of the interfaces
Note: For rackconnect v3, rackconnectv3 needs to be specified with the 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 from __future__ import absolute_import
# pylint: disable=E0102 # 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_): def managedcloud(vm_):
''' '''
Determine if we should wait for the managed cloud automation before Determine if we should wait for the managed cloud automation before
@ -628,10 +639,18 @@ def create(vm_):
networkname = rackconnectv3 networkname = rackconnectv3
for network in node['addresses'].get(networkname, []): for network in node['addresses'].get(networkname, []):
if network['version'] is 4: if network['version'] is 4:
node['extra']['access_ip'] = network['addr'] access_ip = network['addr']
break break
vm_['rackconnect'] = True 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: if rackconnect(vm_) is True:
extra = node.get('extra', {}) extra = node.get('extra', {})
rc_status = extra.get('metadata', {}).get( rc_status = extra.get('metadata', {}).get(
@ -689,6 +708,10 @@ def create(vm_):
data.public_ips = access_ip data.public_ips = access_ip
return data return data
if cloudnetwork(vm_) is True:
data.public_ips = access_ip
return data
if result: if result:
log.debug('result = {0}'.format(result)) log.debug('result = {0}'.format(result))
data.private_ips = result data.private_ips = result

View File

@ -307,6 +307,9 @@ VALID_OPTS = {
# Specify the format for state outputs. See highstate outputter for additional details. # Specify the format for state outputs. See highstate outputter for additional details.
'state_output': str, '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 # When true, states run in the order defined in an SLS file, unless requisites re-order them
'state_auto_order': bool, 'state_auto_order': bool,
@ -700,6 +703,7 @@ VALID_OPTS = {
# Used by salt-api for master requests timeout # Used by salt-api for master requests timeout
'rest_timeout': int, 'rest_timeout': int,
# If set, all minion exec module actions will be rerouted through sudo as this user
'sudo_user': str, 'sudo_user': str,
} }
@ -815,6 +819,7 @@ DEFAULT_MINION_OPTS = {
'cython_enable': False, 'cython_enable': False,
'state_verbose': True, 'state_verbose': True,
'state_output': 'full', 'state_output': 'full',
'state_output_diff': False,
'state_auto_order': True, 'state_auto_order': True,
'state_events': False, 'state_events': False,
'state_aggregate': False, 'state_aggregate': False,
@ -1018,6 +1023,7 @@ DEFAULT_MASTER_OPTS = {
'serial': 'msgpack', 'serial': 'msgpack',
'state_verbose': True, 'state_verbose': True,
'state_output': 'full', 'state_output': 'full',
'state_output_diff': False,
'state_auto_order': True, 'state_auto_order': True,
'state_events': False, 'state_events': False,
'state_aggregate': False, 'state_aggregate': False,

View File

@ -40,10 +40,23 @@ from __future__ import absolute_import
# Import third party libs # Import third party libs
#import salt.ext.six as six #import salt.ext.six as six
# Import salt libs
from salt.exceptions import ( from salt.exceptions import (
#CommandExecutionError, #CommandExecutionError,
SaltInvocationError 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 # pylint: disable=import-error
HAS_GLANCE = False HAS_GLANCE = False
try: try:
@ -77,6 +90,7 @@ def __virtual__():
return 'glance' return 'glance'
return False return False
__opts__ = {} __opts__ = {}
@ -143,12 +157,14 @@ def _auth(profile=None, api_version=2, **connection_args):
'get a new one using username and password.') 'get a new one using username and password.')
if HAS_KEYSTONE: if HAS_KEYSTONE:
# TODO: redact kwargs['password']
log.debug('Calling keystoneclient.v2_0.client.Client(' + log.debug('Calling keystoneclient.v2_0.client.Client(' +
'{0}, **{1})'.format(endpoint, kwargs)) '{0}, **{1})'.format(endpoint, kwargs))
keystone = kstone.Client(**kwargs) keystone = kstone.Client(**kwargs)
log.debug(help(keystone.get_token)) log.debug(help(keystone.get_token))
kwargs['token'] = keystone.get_token(keystone.session) 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') kwargs.pop('password')
log.debug('Calling glanceclient.client.Client(' + log.debug('Calling glanceclient.client.Client(' +
'{0}, {1}, **{2})'.format(api_version, endpoint, kwargs)) '{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") "Can't retrieve a auth_token without keystone")
def image_create(name, location, profile=None, visibility='public', def _add_image(collection, image):
container_format='bare', disk_format='raw'): '''
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) Create an image (glance image-create)
CLI Example: CLI Example, old format:
.. code-block:: bash .. 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 \\ salt '*' glance.image_create name=f16-jeos visibility=public \\
disk_format=qcow2 container_format=ovf \\ disk_format=qcow2 container_format=ovf \\
copy_from=http://berrange.fedorapeople.org/\ copy_from=http://berrange.fedorapeople.org/\
images/2012-02-29/f16-x86_64-openstack-sda.qcow2 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": # valid options for "visibility":
v_list = ['public', 'private'] v_list = ['public', 'private']
# valid options for "container_format": # valid options for "container_format":
@ -181,20 +240,52 @@ def image_create(name, location, profile=None, visibility='public',
# valid options for "disk_format": # valid options for "disk_format":
df_list = ['ami', 'ari', 'aki', 'vhd', 'vmdk', df_list = ['ami', 'ari', 'aki', 'vhd', 'vmdk',
'raw', 'qcow2', 'vdi', 'iso'] 'raw', 'qcow2', 'vdi', 'iso']
if visibility not in v_list: # 'location' and 'visibility' are the parameters used in
raise SaltInvocationError('"visibility" needs to be one ' + # Glance API v2. For now we have to use v1 for now (see below)
'of the following: {0}'.format(', '.join(v_list))) # 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: if container_format not in cf_list:
raise SaltInvocationError('"container_format" needs to be ' + raise SaltInvocationError('"container_format" needs to be ' +
'one of the following: {0}'.format(', '.join(cf_list))) 'one of the following: {0}'.format(', '.join(cf_list)))
else:
kwargs['container_format'] = container_format
if disk_format not in df_list: if disk_format not in df_list:
raise SaltInvocationError('"disk_format" needs to be one ' + raise SaltInvocationError('"disk_format" needs to be one ' +
'of the following: {0}'.format(', '.join(df_list))) '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 # Icehouse's glanceclient doesn't have add_location() and
# glanceclient.v2 doesn't implement Client.images.create() # glanceclient.v2 doesn't implement Client.images.create()
# in a usable fashion. Thus we have to use v1 for now. # in a usable fashion. Thus we have to use v1 for now.
g_client = _auth(profile, api_version=1) 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) 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 id = image.id # pylint: disable=C0103
continue continue
if not id: if not id:
return {'Error': 'Unable to resolve image id'} return {'Error': 'Unable to resolve '
g_client.images.delete(id) '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) ret = 'Deleted image with ID {0}'.format(id)
if name: if name:
ret += ' ({0})'.format(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 pformat = pprint.PrettyPrinter(indent=4).pformat
log.debug('Properties of image {0}:\n{1}'.format( log.debug('Properties of image {0}:\n{1}'.format(
image.name, pformat(image))) image.name, pformat(image)))
# TODO: Get rid of the wrapping dict, see #24568 ret_details = {}
ret[image.name] = {} # 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) schema = image_schema(profile=profile)
if len(schema.keys()) == 1: if len(schema.keys()) == 1:
schema = schema['image'] schema = schema['image']
for key in schema.keys(): for key in schema.keys():
if key in image: if key in image:
ret[image.name][key] = image[key] ret_details[key] = image[key]
return ret 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) 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 salt '*' glance.image_list
''' '''
g_client = _auth(profile) g_client = _auth(profile)
ret = {} # I may want to use this code on Beryllium
# TODO: Get rid of the wrapping dict, see #24568 # 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(): for image in g_client.images.list():
ret[image.name] = { if id is None and name is None:
'id': image.id, _add_image(ret, image)
'name': image.name, else:
'created_at': image.created_at, if id is not None and id == image.id:
'file': image.file, _add_image(ret, image)
'min_disk': image.min_disk, return ret
'min_ram': image.min_ram, if name == image.name:
'owner': image.owner, if name in ret and CUR_VER < BORON:
'protected': image.protected, # Not really worth an exception
'status': image.status, return {'Error': 'More than one image '
'tags': image.tags, 'with name "{0}"'.format(name)}
'updated_at': image.updated_at, _add_image(ret, image)
'visibility': image.visibility, log.debug('Returning images: {0}'.format(ret))
}
# 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]
return ret return ret
@ -304,6 +412,52 @@ def image_schema(profile=None):
return schema_get('image', profile) 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): def schema_get(name, profile=None):
''' '''
Known valid names of schemas are: Known valid names of schemas are:
@ -343,11 +497,10 @@ def _item_list(profile=None):
return ret return ret
#The following is a list of functions that need to be incorporated in the # 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. # glance module. This list should be updated as functions are added.
# image-download Download a specific image. # image-download Download a specific image.
# image-update Update a specific image.
# member-create Share a specific image with a tenant. # member-create Share a specific image with a tenant.
# member-delete Remove a shared image from a tenant. # member-delete Remove a shared image from a tenant.
# member-list Describe sharing permissions by image or tenant. # member-list Describe sharing permissions by image or tenant.

View File

@ -218,7 +218,7 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None,
if 'connstate' in kwargs: if 'connstate' in kwargs:
if '-m state' not in rule: 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'])) rule.append('{0}--state {1}'.format(maybe_add_negation('connstate'), kwargs['connstate']))

View File

@ -376,7 +376,7 @@ def _format_host(host, data):
sum_duration /= 1000 sum_duration /= 1000
duration_unit = 's' duration_unit = 's'
total_duration = u'Total run time: {0} {1}'.format( 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) duration_unit)
hstrs.append(colorfmt.format(colors['CYAN'], total_duration, colors)) hstrs.append(colorfmt.format(colors['CYAN'], total_duration, colors))

View File

@ -144,6 +144,8 @@ def up(): # pylint: disable=C0103
def list_state(subset=None, show_ipv4=False, state=None): 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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) 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', Show minions being in specific state that is one of 'available', 'joined',
'allowed', 'alived' or 'reaped'. 'allowed', 'alived' or 'reaped'.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. 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): 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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent to minions) 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', Show minions being in specific state that is one of 'available', 'joined',
'allowed', 'alived' or 'reaped'. 'allowed', 'alived' or 'reaped'.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. 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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) detection (no commands will be sent to minions)
.. versionadded:: 2015.8.0
subset : None subset : None
Pass in a CIDR range to filter minions by IP address. Pass in a CIDR range to filter minions by IP address.
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -257,6 +253,8 @@ def present(subset=None, show_ipv4=False):
def not_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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent) detection (no commands will be sent)
@ -266,8 +264,6 @@ def not_present(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -279,6 +275,8 @@ def not_present(subset=None, show_ipv4=False):
def joined(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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) detection (no commands will be sent to minions)
@ -288,8 +286,6 @@ def joined(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -301,6 +297,8 @@ def joined(subset=None, show_ipv4=False):
def not_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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent) detection (no commands will be sent)
@ -310,8 +308,6 @@ def not_joined(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -323,6 +319,8 @@ def not_joined(subset=None, show_ipv4=False):
def allowed(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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) detection (no commands will be sent to minions)
@ -332,8 +330,6 @@ def allowed(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -345,6 +341,8 @@ def allowed(subset=None, show_ipv4=False):
def not_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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent) detection (no commands will be sent)
@ -354,8 +352,6 @@ def not_allowed(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -367,6 +363,8 @@ def not_allowed(subset=None, show_ipv4=False):
def alived(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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) detection (no commands will be sent to minions)
@ -376,8 +374,6 @@ def alived(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -389,6 +385,8 @@ def alived(subset=None, show_ipv4=False):
def not_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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent) detection (no commands will be sent)
@ -398,8 +396,6 @@ def not_alived(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -411,6 +407,8 @@ def not_alived(subset=None, show_ipv4=False):
def reaped(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 Print a list of all minions that are up according to Salt's presence
detection (no commands will be sent to minions) detection (no commands will be sent to minions)
@ -420,8 +418,6 @@ def reaped(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
@ -433,6 +429,8 @@ def reaped(subset=None, show_ipv4=False):
def not_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 Print a list of all minions that are NOT up according to Salt's presence
detection (no commands will be sent) detection (no commands will be sent)
@ -442,8 +440,6 @@ def not_reaped(subset=None, show_ipv4=False):
show_ipv4 : False show_ipv4 : False
Also show the IP address each minion is connecting from. Also show the IP address each minion is connecting from.
.. versionadded:: 2015.8.0
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash

250
salt/states/glance.py Normal file
View 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

View File

@ -1586,6 +1586,40 @@ def is_sunos():
return sys.platform.startswith('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 @real_memoize
def is_freebsd(): def is_freebsd():
''' '''

View File

@ -7,11 +7,14 @@ from __future__ import absolute_import
import tornado.ioloop import tornado.ioloop
import tornado.concurrent import tornado.concurrent
LOOP_CLASS = tornado.ioloop.IOLoop
# attempt to use zmq-- if we have it otherwise fallback to tornado loop
try: try:
import zmq.eventloop.ioloop import zmq.eventloop.ioloop
# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x # support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x
if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'):
zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop
LOOP_CLASS = zmq.eventloop.ioloop.ZMQIOLoop
except ImportError: except ImportError:
pass # salt-ssh doesn't dep zmq pass # salt-ssh doesn't dep zmq
@ -47,6 +50,8 @@ class SyncWrapper(object):
ret = sync.async_method() ret = sync.async_method()
''' '''
loop_map = weakref.WeakKeyDictionary() # keep a mapping of parent io_loop -> sync_loop 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): def __init__(self, method, args=tuple(), kwargs=None):
if kwargs is None: if kwargs is None:
@ -54,14 +59,25 @@ class SyncWrapper(object):
parent_io_loop = tornado.ioloop.IOLoop.current() parent_io_loop = tornado.ioloop.IOLoop.current()
if parent_io_loop not in SyncWrapper.loop_map: 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 kwargs['io_loop'] = self.io_loop
with current_ioloop(self.io_loop): with current_ioloop(self.io_loop):
self.async = method(*args, **kwargs) 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): def __getattribute__(self, key):
try: try:
return object.__getattribute__(self, key) return object.__getattribute__(self, key)

View File

@ -2710,8 +2710,9 @@ class SPMParser(six.with_metaclass(OptionParserMeta,
def _mixin_after_parsed(self): def _mixin_after_parsed(self):
# spm needs arguments # spm needs arguments
if len(self.args) <= 1: if len(self.args) <= 1:
self.print_help() if self.args[0] not in ('update_repo',):
self.exit(salt.defaults.exitcodes.EX_USAGE) self.print_help()
self.exit(salt.defaults.exitcodes.EX_USAGE)
def setup_config(self): def setup_config(self):
return salt.config.spm_config(self.get_config_file_path()) return salt.config.spm_config(self.get_config_file_path())

View File

@ -5,6 +5,9 @@ Create and verify ANSI X9.31 RSA signatures using OpenSSL libcrypto
# python libs # python libs
from __future__ import absolute_import from __future__ import absolute_import
import glob
import sys
import os
# salt libs # salt libs
import salt.utils import salt.utils
@ -12,7 +15,6 @@ import salt.utils
# 3rd-party libs # 3rd-party libs
from ctypes import cdll, c_char_p, c_int, c_void_p, pointer, create_string_buffer from ctypes import cdll, c_char_p, c_int, c_void_p, pointer, create_string_buffer
from ctypes.util import find_library from ctypes.util import find_library
import sys
def _load_libcrypto(): def _load_libcrypto():
@ -21,6 +23,10 @@ def _load_libcrypto():
''' '''
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
return cdll.LoadLibrary('libeay32') 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: else:
lib = find_library('crypto') lib = find_library('crypto')
if lib: if lib:

View 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)