mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Merge branch '2017.7' into 'oxygen'
Conflicts: - salt/cloud/clouds/dimensiondata.py - salt/config/__init__.py - salt/modules/yumpkg.py - salt/pillar/ec2_pillar.py - salt/utils/docker/__init__.py - salt/utils/vault.py - tests/integration/states/test_docker_container.py - tests/unit/modules/test_yumpkg.py - tests/unit/templates/test_jinja.py - tests/unit/utils/test_docker.py
This commit is contained in:
commit
e060a74fd8
@ -3,3 +3,78 @@ Salt 2017.7.4 Release Notes
|
||||
===========================
|
||||
|
||||
Version 2017.7.4 is a bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
|
||||
|
||||
Changes for v2017.7.3..v2017.7.4
|
||||
---------------------------------------------------------------
|
||||
|
||||
Extended changelog courtesy of Todd Stansell (https://github.com/tjstansell/salt-changelogs):
|
||||
|
||||
*Generated at: 2018-02-13T16:29:07Z*
|
||||
|
||||
Statistics:
|
||||
|
||||
- Total Merges: **4**
|
||||
- Total Issue references: **3**
|
||||
- Total PR references: **7**
|
||||
|
||||
Changes:
|
||||
|
||||
|
||||
- **PR** `#45981`_: (*gtmanfred*) use local config for vault when masterless
|
||||
@ *2018-02-13T15:22:01Z*
|
||||
|
||||
- **ISSUE** `#45976`_: (*grobinson-blockchain*) 6a5e0f9 introduces regression that breaks Vault module for salt masterless
|
||||
| refs: `#45981`_
|
||||
* ca76a0b328 Merge pull request `#45981`_ from gtmanfred/2017.7.3
|
||||
* 0d448457dc apparently local is not set by default
|
||||
|
||||
* 2a92f4bc16 use local config for vault when masterless
|
||||
|
||||
- **PR** `#45953`_: (*rallytime*) Back-port `#45928`_ to 2017.7.3
|
||||
@ *2018-02-09T22:29:10Z*
|
||||
|
||||
- **ISSUE** `#45915`_: (*MatthiasKuehneEllerhold*) 2017.7.3: Salt-SSH & Vault Pillar: Permission denied "minion.pem"
|
||||
| refs: `#45928`_
|
||||
- **PR** `#45928`_: (*garethgreenaway*) [2017.7] Fixing vault when used with pillar over salt-ssh
|
||||
| refs: `#45953`_
|
||||
* 6530649dbc Merge pull request `#45953`_ from rallytime/`bp-45928`_-2017.7.3
|
||||
* 85363189d1 Fixing vault when used with pillar over salt-ssh
|
||||
|
||||
- **PR** `#45934`_: (*rallytime*) Back-port `#45902`_ to 2017.7.3
|
||||
@ *2018-02-09T16:31:08Z*
|
||||
|
||||
- **ISSUE** `#45893`_: (*CrackerJackMack*) archive.extracted ValueError "No path specified" in 2017.7.3
|
||||
| refs: `#45902`_
|
||||
- **PR** `#45902`_: (*terminalmage*) Check the effective saltenv for cached archive
|
||||
| refs: `#45934`_
|
||||
* fb378cebb0 Merge pull request `#45934`_ from rallytime/`bp-45902`_
|
||||
* bb83e8b345 Add regression test for issue 45893
|
||||
|
||||
* cdda66d759 Remove duplicated section in docstring and fix example
|
||||
|
||||
* 4b6351cda6 Check the effective saltenv for cached archive
|
||||
|
||||
- **PR** `#45935`_: (*rallytime*) Back-port `#45742`_ to 2017.7.3
|
||||
@ *2018-02-09T14:02:26Z*
|
||||
|
||||
- **PR** `#45742`_: (*marccardinal*) list.copy() is not compatible with python 2.7
|
||||
| refs: `#45935`_
|
||||
* 0d74151c71 Merge pull request `#45935`_ from rallytime/`bp-45742`_
|
||||
* 6a0b5f7af3 Removed the chained copy
|
||||
|
||||
* ad1150fad4 list.copy() is not compatible with python 2.7
|
||||
|
||||
|
||||
.. _`#45742`: https://github.com/saltstack/salt/pull/45742
|
||||
.. _`#45893`: https://github.com/saltstack/salt/issues/45893
|
||||
.. _`#45902`: https://github.com/saltstack/salt/pull/45902
|
||||
.. _`#45915`: https://github.com/saltstack/salt/issues/45915
|
||||
.. _`#45928`: https://github.com/saltstack/salt/pull/45928
|
||||
.. _`#45934`: https://github.com/saltstack/salt/pull/45934
|
||||
.. _`#45935`: https://github.com/saltstack/salt/pull/45935
|
||||
.. _`#45953`: https://github.com/saltstack/salt/pull/45953
|
||||
.. _`#45976`: https://github.com/saltstack/salt/issues/45976
|
||||
.. _`#45981`: https://github.com/saltstack/salt/pull/45981
|
||||
.. _`bp-45742`: https://github.com/saltstack/salt/pull/45742
|
||||
.. _`bp-45902`: https://github.com/saltstack/salt/pull/45902
|
||||
.. _`bp-45928`: https://github.com/saltstack/salt/pull/45928
|
||||
|
@ -32,7 +32,7 @@ from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
# Import libcloud
|
||||
try:
|
||||
import libcloud
|
||||
from libcloud.compute.base import NodeState
|
||||
from libcloud.compute.base import NodeDriver, NodeState
|
||||
from libcloud.compute.base import NodeAuthPassword
|
||||
from libcloud.compute.types import Provider
|
||||
from libcloud.compute.providers import get_driver
|
||||
@ -52,9 +52,6 @@ try:
|
||||
except ImportError:
|
||||
HAS_LIBCLOUD = False
|
||||
|
||||
# Import generic libcloud functions
|
||||
# from salt.cloud.libcloudfuncs import *
|
||||
|
||||
# Import salt.cloud libs
|
||||
from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import
|
||||
from salt.utils.functools import namespaced_function
|
||||
@ -217,7 +214,6 @@ def create(vm_):
|
||||
|
||||
log.info('Creating Cloud VM %s', vm_['name'])
|
||||
conn = get_conn()
|
||||
rootPw = NodeAuthPassword(vm_['auth'])
|
||||
|
||||
location = conn.ex_get_location_by_id(vm_['location'])
|
||||
images = conn.list_images(location=location)
|
||||
@ -248,15 +244,13 @@ def create(vm_):
|
||||
kwargs = {
|
||||
'name': vm_['name'],
|
||||
'image': image,
|
||||
'auth': rootPw,
|
||||
'ex_description': vm_['description'],
|
||||
'ex_network_domain': network_domain,
|
||||
'ex_vlan': vlan,
|
||||
'ex_is_started': vm_['is_started']
|
||||
}
|
||||
|
||||
event_data = kwargs.copy()
|
||||
del event_data['auth']
|
||||
event_data = _to_event_data(kwargs)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
@ -267,6 +261,10 @@ def create(vm_):
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
# Initial password (excluded from event payload)
|
||||
initial_password = NodeAuthPassword(vm_['auth'])
|
||||
kwargs['auth'] = initial_password
|
||||
|
||||
try:
|
||||
data = conn.create_node(**kwargs)
|
||||
except Exception as exc:
|
||||
@ -280,7 +278,7 @@ def create(vm_):
|
||||
return False
|
||||
|
||||
try:
|
||||
data = salt.utils.cloud.wait_for_ip(
|
||||
data = __utils__['cloud.wait_for_ip'](
|
||||
_query_node_data,
|
||||
update_args=(vm_, data),
|
||||
timeout=config.get_cloud_config_value(
|
||||
@ -306,7 +304,7 @@ def create(vm_):
|
||||
ip_address = preferred_ip(vm_, data.public_ips)
|
||||
log.debug('Using IP address %s', ip_address)
|
||||
|
||||
if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips':
|
||||
if __utils__['cloud.get_salt_interface'](vm_, __opts__) == 'private_ips':
|
||||
salt_ip_address = preferred_ip(vm_, data.private_ips)
|
||||
log.info('Salt interface set to: %s', salt_ip_address)
|
||||
else:
|
||||
@ -322,7 +320,7 @@ def create(vm_):
|
||||
vm_['ssh_host'] = ip_address
|
||||
vm_['password'] = vm_['auth']
|
||||
|
||||
ret = salt.utils.cloud.bootstrap(vm_, __opts__)
|
||||
ret = __utils__['cloud.bootstrap'](vm_, __opts__)
|
||||
|
||||
ret.update(data.__dict__)
|
||||
|
||||
@ -414,11 +412,13 @@ def create_lb(kwargs=None, call=None):
|
||||
log.debug('Network Domain: %s', network_domain.id)
|
||||
lb_conn.ex_set_current_network_domain(network_domain.id)
|
||||
|
||||
event_data = _to_event_data(kwargs)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'create load_balancer',
|
||||
'salt/cloud/loadbalancer/creating',
|
||||
args=kwargs,
|
||||
args=event_data,
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
@ -427,11 +427,13 @@ def create_lb(kwargs=None, call=None):
|
||||
name, port, protocol, algorithm, members
|
||||
)
|
||||
|
||||
event_data = _to_event_data(kwargs)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'created load_balancer',
|
||||
'salt/cloud/loadbalancer/created',
|
||||
args=kwargs,
|
||||
args=event_data,
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
@ -573,3 +575,46 @@ def get_lb_conn(dd_driver=None):
|
||||
'Missing dimensiondata_driver for get_lb_conn method.'
|
||||
)
|
||||
return get_driver_lb(Provider_lb.DIMENSIONDATA)(user_id, key, region=region)
|
||||
|
||||
|
||||
def _to_event_data(obj):
|
||||
'''
|
||||
Convert the specified object into a form that can be serialised by msgpack as event data.
|
||||
|
||||
:param obj: The object to convert.
|
||||
'''
|
||||
|
||||
if obj is None:
|
||||
return None
|
||||
if isinstance(obj, bool):
|
||||
return obj
|
||||
if isinstance(obj, int):
|
||||
return obj
|
||||
if isinstance(obj, float):
|
||||
return obj
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
if isinstance(obj, bytes):
|
||||
return obj
|
||||
if isinstance(obj, dict):
|
||||
return obj
|
||||
|
||||
if isinstance(obj, NodeDriver): # Special case for NodeDriver (cyclic references)
|
||||
return obj.name
|
||||
|
||||
if isinstance(obj, list):
|
||||
return [_to_event_data(item) for item in obj]
|
||||
|
||||
event_data = {}
|
||||
for attribute_name in dir(obj):
|
||||
if attribute_name.startswith('_'):
|
||||
continue
|
||||
|
||||
attribute_value = getattr(obj, attribute_name)
|
||||
|
||||
if callable(attribute_value): # Strip out methods
|
||||
continue
|
||||
|
||||
event_data[attribute_name] = _to_event_data(attribute_value)
|
||||
|
||||
return event_data
|
||||
|
@ -281,6 +281,7 @@ VALID_OPTS = {
|
||||
|
||||
# Location of the files a minion should look for. Set to 'local' to never ask the master.
|
||||
'file_client': six.string_types,
|
||||
'local': bool,
|
||||
|
||||
# When using a local file_client, this parameter is used to allow the client to connect to
|
||||
# a master for remote execution.
|
||||
@ -1243,6 +1244,7 @@ DEFAULT_MINION_OPTS = {
|
||||
'base': [salt.syspaths.BASE_THORIUM_ROOTS_DIR],
|
||||
},
|
||||
'file_client': 'remote',
|
||||
'local': False,
|
||||
'use_master_when_local': False,
|
||||
'file_roots': {
|
||||
'base': [salt.syspaths.BASE_FILE_ROOTS_DIR,
|
||||
|
@ -34,7 +34,7 @@ def __get_aliases_filename():
|
||||
'''
|
||||
Return the path to the appropriate aliases file
|
||||
'''
|
||||
return __salt__['config.option']('aliases.file')
|
||||
return os.path.realpath(__salt__['config.option']('aliases.file'))
|
||||
|
||||
|
||||
def __parse_aliases():
|
||||
|
@ -422,7 +422,7 @@ def _run(cmd,
|
||||
elif __grains__['os_family'] in ['Solaris']:
|
||||
env_cmd = ('su', '-', runas, '-c', sys.executable)
|
||||
elif __grains__['os_family'] in ['AIX']:
|
||||
env_cmd = ('su', runas, '-c', sys.executable)
|
||||
env_cmd = ('su', '-', runas, '-c', sys.executable)
|
||||
else:
|
||||
env_cmd = ('su', '-s', shell, '-', runas, '-c', sys.executable)
|
||||
env_bytes = salt.utils.stringutils.to_bytes(subprocess.Popen(
|
||||
|
@ -308,13 +308,13 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
|
||||
if pkgs is None:
|
||||
version_num = kwargs.get('version')
|
||||
variant_spec = kwargs.get('variant')
|
||||
spec = None
|
||||
spec = {}
|
||||
|
||||
if version_num:
|
||||
spec = (spec or '') + '@' + version_num
|
||||
spec['version'] = version_num
|
||||
|
||||
if variant_spec:
|
||||
spec = (spec or '') + variant_spec
|
||||
spec['variant'] = variant_spec
|
||||
|
||||
pkg_params = {name: spec}
|
||||
|
||||
@ -323,7 +323,14 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
|
||||
|
||||
formulas_array = []
|
||||
for pname, pparams in six.iteritems(pkg_params):
|
||||
formulas_array.append(pname + (pparams or ''))
|
||||
formulas_array.append(pname)
|
||||
|
||||
if pparams:
|
||||
if 'version' in pparams:
|
||||
formulas_array.append('@' + pparams['version'])
|
||||
|
||||
if 'variant' in pparams:
|
||||
formulas_array.append(pparams['variant'])
|
||||
|
||||
old = list_pkgs()
|
||||
cmd = ['port', 'install']
|
||||
|
@ -456,7 +456,7 @@ def fcontext_get_policy(name, filetype=None, sel_type=None, sel_user=None, sel_l
|
||||
'''
|
||||
if filetype:
|
||||
_validate_filetype(filetype)
|
||||
re_spacer = '[ ]{2,}'
|
||||
re_spacer = '[ ]+'
|
||||
cmd_kwargs = {'spacer': re_spacer,
|
||||
'filespec': re.escape(name),
|
||||
'sel_user': sel_user or '[^:]+',
|
||||
@ -469,11 +469,14 @@ def fcontext_get_policy(name, filetype=None, sel_type=None, sel_user=None, sel_l
|
||||
current_entry_text = __salt__['cmd.shell'](cmd, ignore_retcode=True)
|
||||
if current_entry_text == '':
|
||||
return None
|
||||
ret = {}
|
||||
current_entry_list = re.split(re_spacer, current_entry_text)
|
||||
ret['filespec'] = current_entry_list[0]
|
||||
ret['filetype'] = current_entry_list[1]
|
||||
ret.update(_context_string_to_dict(current_entry_list[2]))
|
||||
|
||||
parts = re.match(r'^({filespec}) +([a-z ]+) (.*)$'.format(**{'filespec': re.escape(name)}), current_entry_text)
|
||||
ret = {
|
||||
'filespec': parts.group(1).strip(),
|
||||
'filetype': parts.group(2).strip(),
|
||||
}
|
||||
ret.update(_context_string_to_dict(parts.group(3).strip()))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@ -517,7 +520,9 @@ def fcontext_add_or_delete_policy(action, name, filetype=None, sel_type=None, se
|
||||
if action not in ['add', 'delete']:
|
||||
raise SaltInvocationError('Actions supported are "add" and "delete", not "{0}".'.format(action))
|
||||
cmd = 'semanage fcontext --{0}'.format(action)
|
||||
if filetype is not None:
|
||||
# "semanage --ftype a" isn't valid on Centos 6,
|
||||
# don't pass --ftype since "a" is the default filetype.
|
||||
if filetype is not None and filetype != 'a':
|
||||
_validate_filetype(filetype)
|
||||
cmd += ' --ftype {0}'.format(filetype)
|
||||
if sel_type is not None:
|
||||
|
@ -926,8 +926,8 @@ def highstate(test=None, queue=False, **kwargs):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.higstate exclude=bar,baz
|
||||
salt '*' state.higstate exclude=foo*
|
||||
salt '*' state.highstate exclude=bar,baz
|
||||
salt '*' state.highstate exclude=foo*
|
||||
salt '*' state.highstate exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]"
|
||||
|
||||
saltenv
|
||||
|
@ -211,25 +211,29 @@ def _check_versionlock():
|
||||
)
|
||||
|
||||
|
||||
def _get_repo_options(**kwargs):
|
||||
def _get_options(**kwargs):
|
||||
'''
|
||||
Returns a list of '--enablerepo' and '--disablerepo' options to be used
|
||||
in the yum command, based on the kwargs.
|
||||
Returns a list of options to be used in the yum/dnf command, based on the
|
||||
kwargs passed.
|
||||
'''
|
||||
# Get repo options from the kwargs
|
||||
fromrepo = kwargs.pop('fromrepo', '')
|
||||
repo = kwargs.pop('repo', '')
|
||||
disablerepo = kwargs.pop('disablerepo', '')
|
||||
enablerepo = kwargs.pop('enablerepo', '')
|
||||
disableexcludes = kwargs.pop('disableexcludes', '')
|
||||
branch = kwargs.pop('branch', '')
|
||||
get_extra_options = kwargs.pop('get_extra_options', False)
|
||||
|
||||
# Support old 'repo' argument
|
||||
if repo and not fromrepo:
|
||||
fromrepo = repo
|
||||
|
||||
ret = []
|
||||
|
||||
if fromrepo:
|
||||
log.info('Restricting to repo \'%s\'', fromrepo)
|
||||
ret.extend(['--disablerepo=*', '--enablerepo=' + fromrepo])
|
||||
ret.extend(['--disablerepo=*', '--enablerepo={0}'.format(fromrepo)])
|
||||
else:
|
||||
if disablerepo:
|
||||
targets = [disablerepo] \
|
||||
@ -245,58 +249,30 @@ def _get_repo_options(**kwargs):
|
||||
else enablerepo
|
||||
log.info('Enabling repo(s): %s', ', '.join(targets))
|
||||
ret.extend(['--enablerepo={0}'.format(x) for x in targets])
|
||||
return ret
|
||||
|
||||
if disableexcludes:
|
||||
log.info('Disabling excludes for \'%s\'', disableexcludes)
|
||||
ret.append('--disableexcludes={0}'.format(disableexcludes))
|
||||
|
||||
def _get_excludes_option(**kwargs):
|
||||
'''
|
||||
Returns a list of '--disableexcludes' option to be used in the yum command,
|
||||
based on the kwargs.
|
||||
'''
|
||||
disable_excludes = kwargs.pop('disableexcludes', '')
|
||||
ret = []
|
||||
if disable_excludes:
|
||||
log.info('Disabling excludes for \'%s\'', disable_excludes)
|
||||
ret.append('--disableexcludes={0}'.format(disable_excludes))
|
||||
return ret
|
||||
|
||||
|
||||
def _get_branch_option(**kwargs):
|
||||
'''
|
||||
Returns a list of '--branch' option to be used in the yum command,
|
||||
based on the kwargs. This feature requires 'branch' plugin for YUM.
|
||||
'''
|
||||
branch = kwargs.pop('branch', '')
|
||||
ret = []
|
||||
if branch:
|
||||
log.info('Adding branch \'%s\'', branch)
|
||||
ret.append('--branch=\'{0}\''.format(branch))
|
||||
return ret
|
||||
ret.append('--branch={0}'.format(branch))
|
||||
|
||||
|
||||
def _get_extra_options(**kwargs):
|
||||
'''
|
||||
Returns list of extra options for yum
|
||||
'''
|
||||
ret = []
|
||||
kwargs = salt.utils.args.clean_kwargs(**kwargs)
|
||||
|
||||
# Remove already handled options from kwargs
|
||||
fromrepo = kwargs.pop('fromrepo', '')
|
||||
repo = kwargs.pop('repo', '')
|
||||
disablerepo = kwargs.pop('disablerepo', '')
|
||||
enablerepo = kwargs.pop('enablerepo', '')
|
||||
disable_excludes = kwargs.pop('disableexcludes', '')
|
||||
branch = kwargs.pop('branch', '')
|
||||
|
||||
for key, value in six.iteritems(kwargs):
|
||||
if get_extra_options:
|
||||
# sorting here to make order uniform, makes unit testing more reliable
|
||||
for key in sorted(kwargs):
|
||||
if key.startswith('__'):
|
||||
continue
|
||||
value = kwargs[key]
|
||||
if isinstance(value, six.string_types):
|
||||
log.info('Adding extra option --%s=\'%s\'', key, value)
|
||||
ret.append('--{0}=\'{1}\''.format(key, value))
|
||||
log.info('Found extra option --%s=%s', key, value)
|
||||
ret.append('--{0}={1}'.format(key, value))
|
||||
elif value is True:
|
||||
log.info('Adding extra option --%s', key)
|
||||
log.info('Found extra option --%s', key)
|
||||
ret.append('--{0}'.format(key))
|
||||
log.info('Adding extra options %s', ret)
|
||||
if ret:
|
||||
log.info('Adding extra options: %s', ret)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@ -460,8 +436,7 @@ def latest_version(*names, **kwargs):
|
||||
if len(names) == 0:
|
||||
return ''
|
||||
|
||||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
options = _get_options(**kwargs)
|
||||
|
||||
# Refresh before looking for the latest version available
|
||||
if refresh:
|
||||
@ -471,8 +446,7 @@ def latest_version(*names, **kwargs):
|
||||
|
||||
# Get available versions for specified package(s)
|
||||
cmd = [_yum(), '--quiet']
|
||||
cmd.extend(repo_arg)
|
||||
cmd.extend(exclude_arg)
|
||||
cmd.extend(options)
|
||||
cmd.extend(['list', 'available'])
|
||||
cmd.extend(names)
|
||||
out = __salt__['cmd.run_all'](cmd,
|
||||
@ -818,7 +792,7 @@ def list_repo_pkgs(*args, **kwargs):
|
||||
disablerepo = kwargs.pop('disablerepo', '') or ''
|
||||
enablerepo = kwargs.pop('enablerepo', '') or ''
|
||||
|
||||
repo_arg = _get_repo_options(fromrepo=fromrepo, **kwargs)
|
||||
repo_arg = _get_options(fromrepo=fromrepo, **kwargs)
|
||||
|
||||
if fromrepo and not isinstance(fromrepo, list):
|
||||
try:
|
||||
@ -970,15 +944,13 @@ def list_upgrades(refresh=True, **kwargs):
|
||||
|
||||
salt '*' pkg.list_upgrades
|
||||
'''
|
||||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
options = _get_options(**kwargs)
|
||||
|
||||
if salt.utils.data.is_true(refresh):
|
||||
refresh_db(check_update=False, **kwargs)
|
||||
|
||||
cmd = [_yum(), '--quiet']
|
||||
cmd.extend(repo_arg)
|
||||
cmd.extend(exclude_arg)
|
||||
cmd.extend(options)
|
||||
cmd.extend(['list', 'upgrades' if _yum() == 'dnf' else 'updates'])
|
||||
out = __salt__['cmd.run_all'](cmd,
|
||||
output_loglevel='trace',
|
||||
@ -1096,21 +1068,19 @@ def refresh_db(**kwargs):
|
||||
|
||||
check_update_ = kwargs.pop('check_update', True)
|
||||
|
||||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
branch_arg = _get_branch_option(**kwargs)
|
||||
options = _get_options(**kwargs)
|
||||
|
||||
clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache']
|
||||
update_cmd = [_yum(), '--quiet', 'check-update']
|
||||
|
||||
if __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '7':
|
||||
# This feature is disable because it is not used by Salt and lasts a lot with using large repo like EPEL
|
||||
if __grains__.get('os_family') == 'RedHat' \
|
||||
and __grains__.get('osmajorrelease') == 7:
|
||||
# This feature is disabled because it is not used by Salt and adds a
|
||||
# lot of extra time to the command with large repos like EPEL
|
||||
update_cmd.append('--setopt=autocheck_running_kernel=false')
|
||||
|
||||
for args in (repo_arg, exclude_arg, branch_arg):
|
||||
if args:
|
||||
clean_cmd.extend(args)
|
||||
update_cmd.extend(args)
|
||||
clean_cmd.extend(options)
|
||||
update_cmd.extend(options)
|
||||
|
||||
__salt__['cmd.run'](clean_cmd, python_shell=False)
|
||||
if check_update_:
|
||||
@ -1162,6 +1132,7 @@ def install(name=None,
|
||||
reinstall=False,
|
||||
normalize=True,
|
||||
update_holds=False,
|
||||
saltenv='base',
|
||||
ignore_epoch=False,
|
||||
**kwargs):
|
||||
'''
|
||||
@ -1343,9 +1314,7 @@ def install(name=None,
|
||||
'version': '<new-version>',
|
||||
'arch': '<new-arch>'}}}
|
||||
'''
|
||||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
branch_arg = _get_branch_option(**kwargs)
|
||||
options = _get_options(**kwargs)
|
||||
|
||||
if salt.utils.data.is_true(refresh):
|
||||
refresh_db(**kwargs)
|
||||
@ -1353,7 +1322,7 @@ def install(name=None,
|
||||
|
||||
try:
|
||||
pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](
|
||||
name, pkgs, sources, normalize=normalize, **kwargs
|
||||
name, pkgs, sources, saltenv=saltenv, normalize=normalize
|
||||
)
|
||||
except MinionError as exc:
|
||||
raise CommandExecutionError(exc)
|
||||
@ -1580,9 +1549,7 @@ def install(name=None,
|
||||
'''
|
||||
DRY function to add args common to all yum/dnf commands
|
||||
'''
|
||||
for arg in (repo_arg, exclude_arg, branch_arg):
|
||||
if arg:
|
||||
cmd.extend(arg)
|
||||
cmd.extend(options)
|
||||
if skip_verify:
|
||||
cmd.append('--nogpgcheck')
|
||||
if downloadonly:
|
||||
@ -1854,10 +1821,7 @@ def upgrade(name=None,
|
||||
salt '*' pkg.upgrade security=True exclude='kernel*'
|
||||
|
||||
'''
|
||||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
branch_arg = _get_branch_option(**kwargs)
|
||||
extra_args = _get_extra_options(**kwargs)
|
||||
options = _get_options(get_extra_options=True, **kwargs)
|
||||
|
||||
if salt.utils.data.is_true(refresh):
|
||||
refresh_db(**kwargs)
|
||||
@ -1886,9 +1850,7 @@ def upgrade(name=None,
|
||||
and __salt__['config.get']('systemd.scope', True):
|
||||
cmd.extend(['systemd-run', '--scope'])
|
||||
cmd.extend([_yum(), '--quiet', '-y'])
|
||||
for args in (repo_arg, exclude_arg, branch_arg, extra_args):
|
||||
if args:
|
||||
cmd.extend(args)
|
||||
cmd.extend(options)
|
||||
if skip_verify:
|
||||
cmd.append('--nogpgcheck')
|
||||
cmd.append('upgrade')
|
||||
|
@ -1,19 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#-*- coding: utf-8 -*-
|
||||
'''
|
||||
Retrieve EC2 instance data for minions.
|
||||
Retrieve EC2 instance data for minions for ec2_tags and ec2_tags_list
|
||||
|
||||
The minion id must be the instance-id retrieved from AWS. As an
|
||||
option, use_grain can be set to True. This allows the use of an
|
||||
The minion id must be the AWS instance-id or value in 'tag_key'.
|
||||
For example set 'tag_key' to 'Name', to have the minion-id matched against the
|
||||
tag 'Name'. The tag contents must be unique. The value of tag_value can
|
||||
be 'uqdn' or 'asis'. if 'uqdn' strips any domain before comparison.
|
||||
|
||||
The option use_grain can be set to True. This allows the use of an
|
||||
instance-id grain instead of the minion-id. Since this is a potential
|
||||
security risk, the configuration can be further expanded to include
|
||||
a list of minions that are trusted to only allow the alternate id
|
||||
of the instances to specific hosts. There is no glob matching at
|
||||
this time.
|
||||
|
||||
The optional 'tag_list_key' indicates which keys should be added to
|
||||
'ec2_tags_list' and be split by tag_list_sep (default `;`). If a tag key is
|
||||
included in 'tag_list_key' it is removed from ec2_tags. If a tag does not
|
||||
exist it is still included as an empty list.
|
||||
|
||||
|
||||
Note: restart the salt-master for changes to take effect.
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ext_pillar:
|
||||
- ec2_pillar:
|
||||
tag_key: 'Name'
|
||||
tag_value: 'asis'
|
||||
tag_list_key:
|
||||
- Role
|
||||
tag_list_sep: ';'
|
||||
use_grain: True
|
||||
minion_ids:
|
||||
- trusted-minion-1
|
||||
@ -31,6 +49,8 @@ the instance.
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import re
|
||||
import logging
|
||||
import salt.ext.six as six
|
||||
from salt.ext.six.moves import range
|
||||
|
||||
# Import salt libs
|
||||
from salt.utils.versions import StrictVersion as _StrictVersion
|
||||
@ -47,6 +67,9 @@ except ImportError:
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# DEBUG boto is far too verbose
|
||||
logging.getLogger('boto').setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
@ -76,64 +99,145 @@ def _get_instance_info():
|
||||
def ext_pillar(minion_id,
|
||||
pillar, # pylint: disable=W0613
|
||||
use_grain=False,
|
||||
minion_ids=None):
|
||||
minion_ids=None,
|
||||
tag_key=None,
|
||||
tag_value='asis',
|
||||
tag_list_key=None,
|
||||
tag_list_sep=';'):
|
||||
'''
|
||||
Execute a command and read the output as YAML
|
||||
'''
|
||||
valid_tag_value = ['uqdn', 'asis']
|
||||
|
||||
log.debug("Querying EC2 tags for minion id %s", minion_id)
|
||||
# meta-data:instance-id
|
||||
grain_instance_id = __grains__.get('meta-data', {}).get('instance-id', None)
|
||||
if not grain_instance_id:
|
||||
# dynamic:instance-identity:document:instanceId
|
||||
grain_instance_id = \
|
||||
__grains__.get('dynamic', {}).get('instance-identity', {}).get('document', {}).get('instance-id', None)
|
||||
if grain_instance_id and re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', grain_instance_id) is None:
|
||||
log.error('External pillar %s, instance-id \'%s\' is not valid for '
|
||||
'\'%s\'', __name__, grain_instance_id, minion_id)
|
||||
grain_instance_id = None # invalid instance id found, remove it from use.
|
||||
|
||||
# If minion_id is not in the format of an AWS EC2 instance, check to see
|
||||
# if there is a grain named 'instance-id' use that. Because this is a
|
||||
# security risk, the master config must contain a use_grain: True option
|
||||
# for this external pillar, which defaults to no
|
||||
if re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', minion_id) is None:
|
||||
if 'instance-id' not in __grains__:
|
||||
log.debug("Minion-id is not in AWS instance-id formation, and there "
|
||||
"is no instance-id grain for minion %s", minion_id)
|
||||
# Check AWS Tag restrictions .i.e. letters, spaces, and numbers and + - = . _ : / @
|
||||
if tag_key and re.match(r'[\w=.:/@-]+$', tag_key) is None:
|
||||
log.error('External pillar %s, tag_key \'%s\' is not valid ',
|
||||
__name__, tag_key if isinstance(tag_key, six.text_type) else 'non-string')
|
||||
return {}
|
||||
if not use_grain:
|
||||
log.debug("Minion-id is not in AWS instance-id formation, and option "
|
||||
"not set to use instance-id grain, for minion %s, use_grain "
|
||||
"is %s", minion_id, use_grain)
|
||||
|
||||
if tag_key and tag_value not in valid_tag_value:
|
||||
log.error('External pillar %s, tag_value \'%s\' is not valid must be one '
|
||||
'of %s', __name__, tag_value, ' '.join(valid_tag_value))
|
||||
return {}
|
||||
log.debug("use_grain set to %s", use_grain)
|
||||
if minion_ids is not None and minion_id not in minion_ids:
|
||||
log.debug("Minion-id is not in AWS instance ID format, and minion_ids "
|
||||
"is set in the ec2_pillar configuration, but minion %s is "
|
||||
"not in the list of allowed minions %s", minion_id, minion_ids)
|
||||
return {}
|
||||
if re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', __grains__['instance-id']) is not None:
|
||||
minion_id = __grains__['instance-id']
|
||||
log.debug("Minion-id is not in AWS instance ID format, but a grain"
|
||||
" is, so using %s as the minion ID", minion_id)
|
||||
|
||||
if not tag_key:
|
||||
base_msg = ('External pillar %s, querying EC2 tags for minion id \'%s\' '
|
||||
'against instance-id', __name__, minion_id)
|
||||
else:
|
||||
log.debug("Nether minion id nor a grain named instance-id is in "
|
||||
"AWS format, can't query EC2 tags for minion %s", minion_id)
|
||||
base_msg = ('External pillar %s, querying EC2 tags for minion id \'%s\' '
|
||||
'against instance-id or \'%s\' against \'%s\'', __name__, minion_id, tag_key, tag_value)
|
||||
|
||||
log.debug(base_msg)
|
||||
find_filter = None
|
||||
find_id = None
|
||||
|
||||
if re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', minion_id) is not None:
|
||||
find_filter = None
|
||||
find_id = minion_id
|
||||
elif tag_key:
|
||||
if tag_value == 'uqdn':
|
||||
find_filter = {'tag:{0}'.format(tag_key): minion_id.split('.', 1)[0]}
|
||||
else:
|
||||
find_filter = {'tag:{0}'.format(tag_key): minion_id}
|
||||
if grain_instance_id:
|
||||
# we have an untrusted grain_instance_id, use it to narrow the search
|
||||
# even more. Combination will be unique even if uqdn is set.
|
||||
find_filter.update({'instance-id': grain_instance_id})
|
||||
# Add this if running state is not dependant on EC2Config
|
||||
# find_filter.update('instance-state-name': 'running')
|
||||
|
||||
# no minion-id is instance-id and no suitable filter, try use_grain if enabled
|
||||
if not find_filter and not find_id and use_grain:
|
||||
if not grain_instance_id:
|
||||
log.debug('Minion-id is not in AWS instance-id formation, and there '
|
||||
'is no instance-id grain for minion %s', minion_id)
|
||||
return {}
|
||||
if minion_ids is not None and minion_id not in minion_ids:
|
||||
log.debug('Minion-id is not in AWS instance ID format, and minion_ids '
|
||||
'is set in the ec2_pillar configuration, but minion %s is '
|
||||
'not in the list of allowed minions %s', minion_id, minion_ids)
|
||||
return {}
|
||||
find_id = grain_instance_id
|
||||
|
||||
if not (find_filter or find_id):
|
||||
log.debug('External pillar %s, querying EC2 tags for minion id \'%s\' against '
|
||||
'instance-id or \'%s\' against \'%s\' noughthing to match against',
|
||||
__name__, minion_id, tag_key, tag_value)
|
||||
return {}
|
||||
|
||||
m = boto.utils.get_instance_metadata(timeout=0.1, num_retries=1)
|
||||
if len(m.keys()) < 1:
|
||||
log.info("%s: not an EC2 instance, skipping", __name__)
|
||||
return None
|
||||
myself = boto.utils.get_instance_metadata(timeout=0.1, num_retries=1)
|
||||
if len(myself.keys()) < 1:
|
||||
log.info("%s: salt master not an EC2 instance, skipping", __name__)
|
||||
return {}
|
||||
|
||||
# Get the Master's instance info, primarily the region
|
||||
(instance_id, region) = _get_instance_info()
|
||||
(_, region) = _get_instance_info()
|
||||
|
||||
try:
|
||||
conn = boto.ec2.connect_to_region(region)
|
||||
except boto.exception as e: # pylint: disable=E0712
|
||||
log.error("%s: invalid AWS credentials.", __name__)
|
||||
return None
|
||||
except boto.exception.AWSConnectionError as exc:
|
||||
log.error('%s: invalid AWS credentials, %s', __name__, exc)
|
||||
return {}
|
||||
except:
|
||||
raise
|
||||
|
||||
if conn is None:
|
||||
log.error('%s: Could not connect to region %s', __name__, region)
|
||||
return {}
|
||||
|
||||
tags = {}
|
||||
try:
|
||||
_tags = conn.get_all_tags(filters={'resource-type': 'instance',
|
||||
'resource-id': minion_id})
|
||||
for tag in _tags:
|
||||
tags[tag.name] = tag.value
|
||||
except IndexError as e:
|
||||
log.error("Couldn't retrieve instance information: %s", e)
|
||||
return None
|
||||
if find_id:
|
||||
instance_data = conn.get_only_instances(instance_ids=[find_id], dry_run=False)
|
||||
else:
|
||||
# filters and max_results can not be used togther.
|
||||
instance_data = conn.get_only_instances(filters=find_filter, dry_run=False)
|
||||
|
||||
return {'ec2_tags': tags}
|
||||
except boto.exception.EC2ResponseError as exc:
|
||||
log.error('%s failed with \'%s\'', base_msg, exc)
|
||||
return {}
|
||||
|
||||
if not instance_data:
|
||||
log.debug('%s no match using \'%s\'', base_msg, find_id if find_id else find_filter)
|
||||
return {}
|
||||
|
||||
# Find a active instance, i.e. ignore terminated and stopped instances
|
||||
active_inst = []
|
||||
for inst in range(0, len(instance_data)):
|
||||
if instance_data[inst].state not in ['terminated', 'stopped']:
|
||||
active_inst.append(inst)
|
||||
|
||||
valid_inst = len(active_inst)
|
||||
if not valid_inst:
|
||||
log.debug('%s match found but not active \'%s\'', base_msg, find_id if find_id else find_filter)
|
||||
return {}
|
||||
|
||||
if valid_inst > 1:
|
||||
log.error('%s multiple matches, ignored, using \'%s\'', base_msg, find_id if find_id else find_filter)
|
||||
return {}
|
||||
|
||||
instance = instance_data[active_inst[0]]
|
||||
if instance.tags:
|
||||
ec2_tags = instance.tags
|
||||
ec2_tags_list = {}
|
||||
log.debug('External pillar %s, for minion id \'%s\', tags: %s', __name__, minion_id, instance.tags)
|
||||
if tag_list_key and isinstance(tag_list_key, list):
|
||||
for item in tag_list_key:
|
||||
if item in ec2_tags:
|
||||
ec2_tags_list[item] = ec2_tags[item].split(tag_list_sep)
|
||||
del ec2_tags[item] # make sure its only in ec2_tags_list
|
||||
else:
|
||||
ec2_tags_list[item] = [] # always return a result
|
||||
|
||||
return {'ec2_tags': ec2_tags, 'ec2_tags_list': ec2_tags_list}
|
||||
return {}
|
||||
|
@ -1,8 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
The module used to execute states in salt. A state is unlike a module
|
||||
execution in that instead of just executing a command it ensure that a
|
||||
certain state is present on the system.
|
||||
The State Compiler is used to execute states in Salt. A state is unlike
|
||||
an execution module in that instead of just executing a command, it
|
||||
ensures that a certain state is present on the system.
|
||||
|
||||
The data sent to the state calls is as follows:
|
||||
{ 'state': '<state module name>',
|
||||
|
@ -124,7 +124,7 @@ def _get_vault_connection():
|
||||
|
||||
if 'vault' in __opts__ and __opts__.get('__role', 'minion') == 'master':
|
||||
return _use_local_config()
|
||||
elif '_ssh_version' in __opts__:
|
||||
elif any((__opts__['local'], __opts__['file_client'] == 'local', __opts__['master_type'] == 'disable')):
|
||||
return _use_local_config()
|
||||
else:
|
||||
log.debug('Contacting master for Vault connection details')
|
||||
|
137
tests/integration/cloud/providers/test_dimensiondata.py
Normal file
137
tests/integration/cloud/providers/test_dimensiondata.py
Normal file
@ -0,0 +1,137 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Integration tests for the Dimension Data cloud provider
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.case import ShellCase
|
||||
from tests.support.paths import FILES
|
||||
from tests.support.helpers import expensiveTest
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.config import cloud_providers_config
|
||||
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
|
||||
|
||||
# Create the cloud instance name to be used throughout the tests
|
||||
INSTANCE_NAME = _random_name('CLOUD-TEST-')
|
||||
PROVIDER_NAME = 'dimensiondata'
|
||||
|
||||
|
||||
def _random_name(size=6):
|
||||
'''
|
||||
Generates a random cloud instance name
|
||||
'''
|
||||
return 'cloud-test-' + ''.join(
|
||||
random.choice(string.ascii_lowercase + string.digits)
|
||||
for x in range(size)
|
||||
)
|
||||
|
||||
|
||||
class DimensionDataTest(ShellCase):
|
||||
'''
|
||||
Integration tests for the Dimension Data cloud provider in Salt-Cloud
|
||||
'''
|
||||
|
||||
@expensiveTest
|
||||
def setUp(self):
|
||||
'''
|
||||
Sets up the test requirements
|
||||
'''
|
||||
super(DimensionDataTest, self).setUp()
|
||||
|
||||
# check if appropriate cloud provider and profile files are present
|
||||
profile_str = 'dimensiondata-config'
|
||||
providers = self.run_cloud('--list-providers')
|
||||
if profile_str + ':' not in providers:
|
||||
self.skipTest(
|
||||
'Configuration file for {0} was not found. Check {0}.conf files '
|
||||
'in tests/integration/files/conf/cloud.*.d/ to run these tests.'
|
||||
.format(PROVIDER_NAME)
|
||||
)
|
||||
|
||||
# check if user_id, key, and region are present
|
||||
config = cloud_providers_config(
|
||||
os.path.join(
|
||||
FILES,
|
||||
'conf',
|
||||
'cloud.providers.d',
|
||||
PROVIDER_NAME + '.conf'
|
||||
)
|
||||
)
|
||||
|
||||
user_id = config[profile_str][PROVIDER_NAME]['user_id']
|
||||
key = config[profile_str][PROVIDER_NAME]['key']
|
||||
region = config[profile_str][PROVIDER_NAME]['region']
|
||||
|
||||
if user_id == '' or key == '' or region == '':
|
||||
self.skipTest(
|
||||
'A user Id, password, and a region '
|
||||
'must be provided to run these tests. Check '
|
||||
'tests/integration/files/conf/cloud.providers.d/{0}.conf'
|
||||
.format(PROVIDER_NAME)
|
||||
)
|
||||
|
||||
def test_list_images(self):
|
||||
'''
|
||||
Tests the return of running the --list-images command for the dimensiondata cloud provider
|
||||
'''
|
||||
image_list = self.run_cloud('--list-images {0}'.format(PROVIDER_NAME))
|
||||
self.assertIn(
|
||||
'Ubuntu 14.04 2 CPU',
|
||||
[i.strip() for i in image_list]
|
||||
)
|
||||
|
||||
def test_list_locations(self):
|
||||
'''
|
||||
Tests the return of running the --list-locations command for the dimensiondata cloud provider
|
||||
'''
|
||||
_list_locations = self.run_cloud('--list-locations {0}'.format(PROVIDER_NAME))
|
||||
self.assertIn(
|
||||
'Australia - Melbourne MCP2',
|
||||
[i.strip() for i in _list_locations]
|
||||
)
|
||||
|
||||
def test_list_sizes(self):
|
||||
'''
|
||||
Tests the return of running the --list-sizes command for the dimensiondata cloud provider
|
||||
'''
|
||||
_list_sizes = self.run_cloud('--list-sizes {0}'.format(PROVIDER_NAME))
|
||||
self.assertIn(
|
||||
'default',
|
||||
[i.strip() for i in _list_sizes]
|
||||
)
|
||||
|
||||
def test_instance(self):
|
||||
'''
|
||||
Test creating an instance on Dimension Data's cloud
|
||||
'''
|
||||
# check if instance with salt installed returned
|
||||
try:
|
||||
self.assertIn(
|
||||
INSTANCE_NAME,
|
||||
[i.strip() for i in self.run_cloud('-p dimensiondata-test {0}'.format(INSTANCE_NAME), timeout=500)]
|
||||
)
|
||||
except AssertionError:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
raise
|
||||
|
||||
# delete the instance
|
||||
try:
|
||||
self.assertIn(
|
||||
'True',
|
||||
[i.strip() for i in self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)]
|
||||
)
|
||||
except AssertionError:
|
||||
raise
|
||||
|
||||
# Final clean-up of created instance, in case something went wrong.
|
||||
# This was originally in a tearDown function, but that didn't make sense
|
||||
# To run this for each test when not all tests create instances.
|
||||
if INSTANCE_NAME in [i.strip() for i in self.run_cloud('--query')]:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
@ -0,0 +1,11 @@
|
||||
dimensiondata-test:
|
||||
provider: dimensiondata-config
|
||||
image: 42816eb2-9846-4483-95c3-7d7fbddebf2c
|
||||
size: default
|
||||
location: AU10
|
||||
is_started: yes
|
||||
description: 'Salt Ubuntu test'
|
||||
network_domain: ''
|
||||
vlan: ''
|
||||
ssh_interface: private_ips
|
||||
auth: ''
|
@ -0,0 +1,5 @@
|
||||
dimensiondata-config:
|
||||
driver: dimensiondata
|
||||
user_id: ''
|
||||
key: ''
|
||||
region: 'dd-au'
|
87
tests/unit/modules/test_selinux.py
Normal file
87
tests/unit/modules/test_selinux.py
Normal file
@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from __future__ import absolute_import
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
# Import Salt libs
|
||||
import salt.modules.selinux as selinux
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class SelinuxModuleTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.modules.selinux
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {selinux: {}}
|
||||
|
||||
def test_fcontext_get_policy_parsing(self):
|
||||
'''
|
||||
Test to verify that the parsing of the semanage output into fields is
|
||||
correct. Added with #45784.
|
||||
'''
|
||||
cases = [
|
||||
{
|
||||
'semanage_out': '/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0',
|
||||
'name': '/var/www(/.*)?',
|
||||
'filetype': 'all files',
|
||||
'sel_user': 'system_u',
|
||||
'sel_role': 'object_r',
|
||||
'sel_type': 'httpd_sys_content_t',
|
||||
'sel_level': 's0'
|
||||
},
|
||||
{
|
||||
'semanage_out': '/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0',
|
||||
'name': '/var/www(/.*)?',
|
||||
'filetype': 'all files',
|
||||
'sel_user': 'system_u',
|
||||
'sel_role': 'object_r',
|
||||
'sel_type': 'httpd_sys_content_t',
|
||||
'sel_level': 's0'
|
||||
},
|
||||
{
|
||||
'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0',
|
||||
'name': '/var/lib/dhcp3?',
|
||||
'filetype': 'directory',
|
||||
'sel_user': 'system_u',
|
||||
'sel_role': 'object_r',
|
||||
'sel_type': 'dhcp_state_t',
|
||||
'sel_level': 's0'
|
||||
},
|
||||
{
|
||||
'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0',
|
||||
'name': '/var/lib/dhcp3?',
|
||||
'filetype': 'directory',
|
||||
'sel_user': 'system_u',
|
||||
'sel_role': 'object_r',
|
||||
'sel_type': 'dhcp_state_t',
|
||||
'sel_level': 's0'
|
||||
},
|
||||
{
|
||||
'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0',
|
||||
'name': '/var/lib/dhcp3?',
|
||||
'filetype': 'directory',
|
||||
'sel_user': 'system_u',
|
||||
'sel_role': 'object_r',
|
||||
'sel_type': 'dhcp_state_t',
|
||||
'sel_level': 's0'
|
||||
}
|
||||
]
|
||||
|
||||
for case in cases:
|
||||
with patch.dict(selinux.__salt__, {'cmd.shell': MagicMock(return_value=case['semanage_out'])}):
|
||||
ret = selinux.fcontext_get_policy(case['name'])
|
||||
self.assertEqual(ret['filespec'], case['name'])
|
||||
self.assertEqual(ret['filetype'], case['filetype'])
|
||||
self.assertEqual(ret['sel_user'], case['sel_user'])
|
||||
self.assertEqual(ret['sel_role'], case['sel_role'])
|
||||
self.assertEqual(ret['sel_type'], case['sel_type'])
|
||||
self.assertEqual(ret['sel_level'], case['sel_level'])
|
@ -8,6 +8,7 @@ import os
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
Mock,
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
@ -18,6 +19,39 @@ from tests.support.mock import (
|
||||
import salt.modules.yumpkg as yumpkg
|
||||
import salt.modules.pkg_resource as pkg_resource
|
||||
|
||||
LIST_REPOS = {
|
||||
'base': {
|
||||
'file': '/etc/yum.repos.d/CentOS-Base.repo',
|
||||
'gpgcheck': '1',
|
||||
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
|
||||
'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra',
|
||||
'name': 'CentOS-$releasever - Base'
|
||||
},
|
||||
'base-source': {
|
||||
'baseurl': 'http://vault.centos.org/centos/$releasever/os/Source/',
|
||||
'enabled': '0',
|
||||
'file': '/etc/yum.repos.d/CentOS-Sources.repo',
|
||||
'gpgcheck': '1',
|
||||
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
|
||||
'name': 'CentOS-$releasever - Base Sources'
|
||||
},
|
||||
'updates': {
|
||||
'file': '/etc/yum.repos.d/CentOS-Base.repo',
|
||||
'gpgcheck': '1',
|
||||
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
|
||||
'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra',
|
||||
'name': 'CentOS-$releasever - Updates'
|
||||
},
|
||||
'updates-source': {
|
||||
'baseurl': 'http://vault.centos.org/centos/$releasever/updates/Source/',
|
||||
'enabled': '0',
|
||||
'file': '/etc/yum.repos.d/CentOS-Sources.repo',
|
||||
'gpgcheck': '1',
|
||||
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
|
||||
'name': 'CentOS-$releasever - Updates Sources'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class YumTestCase(TestCase, LoaderModuleMockMixin):
|
||||
@ -25,7 +59,18 @@ class YumTestCase(TestCase, LoaderModuleMockMixin):
|
||||
Test cases for salt.modules.yumpkg
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {yumpkg: {'rpm': None}}
|
||||
return {
|
||||
yumpkg: {
|
||||
'__context__': {
|
||||
'yum_bin': 'yum',
|
||||
},
|
||||
'__grains__': {
|
||||
'osarch': 'x86_64',
|
||||
'os_family': 'RedHat',
|
||||
'osmajorrelease': 7,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
def test_list_pkgs(self):
|
||||
'''
|
||||
@ -186,3 +231,373 @@ class YumTestCase(TestCase, LoaderModuleMockMixin):
|
||||
}}.items():
|
||||
self.assertTrue(pkgs.get(pkg_name))
|
||||
self.assertEqual(pkgs[pkg_name], [pkg_attr])
|
||||
|
||||
def test_latest_version_with_options(self):
|
||||
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})):
|
||||
|
||||
# with fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.latest_version(
|
||||
'foo',
|
||||
refresh=False,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '--disablerepo=*', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'available', 'foo'],
|
||||
ignore_retcode=True,
|
||||
output_loglevel='trace',
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.latest_version(
|
||||
'foo',
|
||||
refresh=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'available', 'foo'],
|
||||
ignore_retcode=True,
|
||||
output_loglevel='trace',
|
||||
python_shell=False)
|
||||
|
||||
def test_list_repo_pkgs_with_options(self):
|
||||
'''
|
||||
Test list_repo_pkgs with and without fromrepo
|
||||
|
||||
NOTE: mock_calls is a stack. The most recent call is indexed
|
||||
with 0, while the first call would have the highest index.
|
||||
'''
|
||||
really_old_yum = MagicMock(return_value='3.2.0')
|
||||
older_yum = MagicMock(return_value='3.4.0')
|
||||
newer_yum = MagicMock(return_value='3.4.5')
|
||||
list_repos_mock = MagicMock(return_value=LIST_REPOS)
|
||||
kwargs = {'output_loglevel': 'trace',
|
||||
'ignore_retcode': True,
|
||||
'python_shell': False}
|
||||
|
||||
with patch.object(yumpkg, 'list_repos', list_repos_mock):
|
||||
|
||||
# Test with really old yum. The fromrepo argument has no effect on
|
||||
# the yum commands we'd run.
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': really_old_yum}):
|
||||
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_repo_pkgs('foo')
|
||||
# We should have called cmd.run_all twice
|
||||
self.assertEqual(len(cmd.mock_calls), 2)
|
||||
|
||||
# Check args from first call
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[1][1],
|
||||
(['yum', '--quiet', 'list', 'available'],)
|
||||
)
|
||||
# Check kwargs from first call
|
||||
self.assertEqual(cmd.mock_calls[1][2], kwargs)
|
||||
|
||||
# Check args from second call
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[0][1],
|
||||
(['yum', '--quiet', 'list', 'installed'],)
|
||||
)
|
||||
# Check kwargs from second call
|
||||
self.assertEqual(cmd.mock_calls[0][2], kwargs)
|
||||
|
||||
# Test with really old yum. The fromrepo argument has no effect on
|
||||
# the yum commands we'd run.
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': older_yum}):
|
||||
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_repo_pkgs('foo')
|
||||
# We should have called cmd.run_all twice
|
||||
self.assertEqual(len(cmd.mock_calls), 2)
|
||||
|
||||
# Check args from first call
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[1][1],
|
||||
(['yum', '--quiet', '--showduplicates', 'list', 'available'],)
|
||||
)
|
||||
# Check kwargs from first call
|
||||
self.assertEqual(cmd.mock_calls[1][2], kwargs)
|
||||
|
||||
# Check args from second call
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[0][1],
|
||||
(['yum', '--quiet', '--showduplicates', 'list', 'installed'],)
|
||||
)
|
||||
# Check kwargs from second call
|
||||
self.assertEqual(cmd.mock_calls[0][2], kwargs)
|
||||
|
||||
# Test with newer yum. We should run one yum command per repo, so
|
||||
# fromrepo would limit how many calls we make.
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': newer_yum}):
|
||||
|
||||
# When fromrepo is used, we would only run one yum command, for
|
||||
# that specific repo.
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_repo_pkgs('foo', fromrepo='base')
|
||||
# We should have called cmd.run_all once
|
||||
self.assertEqual(len(cmd.mock_calls), 1)
|
||||
|
||||
# Check args
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[0][1],
|
||||
(['yum', '--quiet', '--showduplicates',
|
||||
'repository-packages', 'base', 'list', 'foo'],)
|
||||
)
|
||||
# Check kwargs
|
||||
self.assertEqual(cmd.mock_calls[0][2], kwargs)
|
||||
|
||||
# Test enabling base-source and disabling updates. We should
|
||||
# get two calls, one for each enabled repo. Because dict
|
||||
# iteration order will vary, different Python versions will be
|
||||
# do them in different orders, which is OK, but it will just
|
||||
# mean that we will have to check both the first and second
|
||||
# mock call both times.
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_repo_pkgs(
|
||||
'foo',
|
||||
enablerepo='base-source',
|
||||
disablerepo='updates')
|
||||
# We should have called cmd.run_all twice
|
||||
self.assertEqual(len(cmd.mock_calls), 2)
|
||||
|
||||
for repo in ('base', 'base-source'):
|
||||
for index in (0, 1):
|
||||
try:
|
||||
# Check args
|
||||
self.assertEqual(
|
||||
cmd.mock_calls[index][1],
|
||||
(['yum', '--quiet', '--showduplicates',
|
||||
'repository-packages', repo, 'list',
|
||||
'foo'],)
|
||||
)
|
||||
# Check kwargs
|
||||
self.assertEqual(cmd.mock_calls[index][2], kwargs)
|
||||
break
|
||||
except AssertionError:
|
||||
continue
|
||||
else:
|
||||
self.fail("repo '{0}' not checked".format(repo))
|
||||
|
||||
def test_list_upgrades_dnf(self):
|
||||
'''
|
||||
The subcommand should be "upgrades" with dnf
|
||||
'''
|
||||
with patch.dict(yumpkg.__context__, {'yum_bin': 'dnf'}):
|
||||
# with fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_upgrades(
|
||||
refresh=False,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['dnf', '--quiet', '--disablerepo=*', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'upgrades'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_upgrades(
|
||||
refresh=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['dnf', '--quiet', '--disablerepo=bad', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'upgrades'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
def test_list_upgrades_yum(self):
|
||||
'''
|
||||
The subcommand should be "updates" with yum
|
||||
'''
|
||||
# with fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_upgrades(
|
||||
refresh=False,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '--disablerepo=*', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'updates'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.list_upgrades(
|
||||
refresh=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good',
|
||||
'--branch=foo', 'list', 'updates'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
def test_refresh_db_with_options(self):
|
||||
|
||||
with patch('salt.utils.pkg.clear_rtag', Mock()):
|
||||
|
||||
# With check_update=True we will do a cmd.run to run the clean_cmd, and
|
||||
# then a separate cmd.retcode to check for updates.
|
||||
|
||||
# with fromrepo
|
||||
clean_cmd = Mock()
|
||||
update_cmd = MagicMock(return_value=0)
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd,
|
||||
'cmd.retcode': update_cmd}):
|
||||
yumpkg.refresh_db(
|
||||
check_update=True,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
clean_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=*',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
python_shell=False)
|
||||
update_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'check-update',
|
||||
'--setopt=autocheck_running_kernel=false', '--disablerepo=*',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
clean_cmd = Mock()
|
||||
update_cmd = MagicMock(return_value=0)
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd,
|
||||
'cmd.retcode': update_cmd}):
|
||||
yumpkg.refresh_db(
|
||||
check_update=True,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
clean_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=bad',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
python_shell=False)
|
||||
update_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'check-update',
|
||||
'--setopt=autocheck_running_kernel=false', '--disablerepo=bad',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
|
||||
# With check_update=False we will just do a cmd.run for the clean_cmd
|
||||
|
||||
# with fromrepo
|
||||
clean_cmd = Mock()
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd}):
|
||||
yumpkg.refresh_db(
|
||||
check_update=False,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
clean_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=*',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
clean_cmd = Mock()
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd}):
|
||||
yumpkg.refresh_db(
|
||||
check_update=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
clean_cmd.assert_called_once_with(
|
||||
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=bad',
|
||||
'--enablerepo=good', '--branch=foo'],
|
||||
python_shell=False)
|
||||
|
||||
def test_install_with_options(self):
|
||||
parse_targets = MagicMock(return_value=({'foo': None}, 'repository'))
|
||||
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \
|
||||
patch.object(yumpkg, 'list_holds', MagicMock(return_value=[])), \
|
||||
patch.dict(yumpkg.__salt__, {'pkg_resource.parse_targets': parse_targets}), \
|
||||
patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)):
|
||||
|
||||
# with fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.install(
|
||||
refresh=False,
|
||||
fromrepo='good',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '-y', '--disablerepo=*', '--enablerepo=good',
|
||||
'--branch=foo', 'install', 'foo'],
|
||||
output_loglevel='trace',
|
||||
python_shell=False,
|
||||
redirect_stderr=True)
|
||||
|
||||
# without fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.install(
|
||||
refresh=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '-y', '--disablerepo=bad', '--enablerepo=good',
|
||||
'--branch=foo', 'install', 'foo'],
|
||||
output_loglevel='trace',
|
||||
python_shell=False,
|
||||
redirect_stderr=True)
|
||||
|
||||
def test_upgrade_with_options(self):
|
||||
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \
|
||||
patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)):
|
||||
|
||||
# with fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.upgrade(
|
||||
refresh=False,
|
||||
fromrepo='good',
|
||||
exclude='kernel*',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '-y', '--disablerepo=*', '--enablerepo=good',
|
||||
'--branch=foo', '--exclude=kernel*', 'upgrade'],
|
||||
output_loglevel='trace',
|
||||
python_shell=False)
|
||||
|
||||
# without fromrepo
|
||||
cmd = MagicMock(return_value={'retcode': 0})
|
||||
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
|
||||
yumpkg.upgrade(
|
||||
refresh=False,
|
||||
enablerepo='good',
|
||||
disablerepo='bad',
|
||||
exclude='kernel*',
|
||||
branch='foo')
|
||||
cmd.assert_called_once_with(
|
||||
['yum', '--quiet', '-y', '--disablerepo=bad', '--enablerepo=good',
|
||||
'--branch=foo', '--exclude=kernel*', 'upgrade'],
|
||||
output_loglevel='trace',
|
||||
python_shell=False)
|
||||
|
@ -773,6 +773,29 @@ class TranslateBase(TestCase):
|
||||
ret[key] = val
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def normalize_ports(ret):
|
||||
'''
|
||||
When we translate exposed ports, we can end up with a mixture of ints
|
||||
(representing TCP ports) and tuples (representing UDP ports). Python 2
|
||||
will sort an iterable containing these mixed types, but Python 3 will
|
||||
not. This helper is used to munge the ports in the return data so that
|
||||
the resulting list is sorted in a way that can reliably be compared to
|
||||
the expected results in the test.
|
||||
|
||||
This helper should only be needed for port_bindings and ports.
|
||||
'''
|
||||
if 'ports' in ret[0]:
|
||||
tcp_ports = []
|
||||
udp_ports = []
|
||||
for item in ret[0]['ports']:
|
||||
if isinstance(item, six.integer_types):
|
||||
tcp_ports.append(item)
|
||||
else:
|
||||
udp_ports.append(item)
|
||||
ret[0]['ports'] = sorted(tcp_ports) + sorted(udp_ports)
|
||||
return ret
|
||||
|
||||
def tearDown(self):
|
||||
'''
|
||||
Test skip_translate kwarg
|
||||
|
Loading…
Reference in New Issue
Block a user