From 624f25b78d20b10b551e779f4921e08f36463be1 Mon Sep 17 00:00:00 2001 From: Frederic Boismenu Date: Tue, 17 Jan 2017 16:51:07 -0500 Subject: [PATCH 01/14] Fix for #38697 --- salt/modules/win_ip.py | 86 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index 4729099b5c1f..e06b3b3c747d 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -41,65 +41,57 @@ def _interface_configs(): ''' cmd = ['netsh', 'interface', 'ip', 'show', 'config'] lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - iface = '' - ip = 0 - dns_flag = None - wins_flag = None ret = {} + current_iface = None + current_ip_list = None + for line in lines: - if dns_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][dns_flag].append(line.strip()) - dns_flag = None - continue - except socket.error as exc: - dns_flag = None - if wins_flag: - try: - socket.inet_aton(line.strip()) - ret[iface][wins_flag].append(line.strip()) - wins_flag = None - continue - except socket.error as exc: - wins_flag = None + + line = line.strip() if not line: - iface = '' + current_iface = None + current_ip_list = None continue + if 'Configuration for interface' in line: _, iface = line.rstrip('"').split('"', 1) # get iface name - ret[iface] = {} - ip = 0 - continue - try: - key, val = line.split(':', 1) - except ValueError as exc: - log.debug('Could not split line. Error was {0}.'.format(exc)) + current_iface = {} + ret[iface] = current_iface continue - if 'DNS Servers' in line: - dns_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'WINS Servers' in line: - wins_flag = key.strip() - ret[iface][key.strip()] = [val.strip()] - continue - if 'IP Address' in key: - if 'ip_addrs' not in ret[iface]: - ret[iface]['ip_addrs'] = [] - ret[iface]['ip_addrs'].append(dict([(key.strip(), val.strip())])) - continue - if 'Subnet Prefix' in key: - subnet, _, netmask = val.strip().split(' ', 2) - ret[iface]['ip_addrs'][ip]['Subnet'] = subnet.strip() - ret[iface]['ip_addrs'][ip]['Netmask'] = netmask.lstrip().rstrip(')') - ip = ip + 1 + + if ':' not in line: + if current_ip_list: + current_ip_list.append( line ) + else: + log.warning( 'Cannot parse "{0}"'.format(line) ) continue + + key, val = line.split(':', 1) + key = key.strip() + val = val.strip() + + lkey = key.lower() + if ('dns servers' in lkey) or ('wins servers' in lkey): + current_ip_list = [] + current_iface[key] = current_ip_list + current_ip_list.append( val ) + + elif 'ip address' in lkey: + current_iface.setdefault( 'ip_addrs', [] ).append( {key:val} ) + + elif 'subnet prefix' in lkey: + subnet, _, netmask = val.split(' ', 2) + last_ip = current_iface['ip_addrs'][-1] + last_ip['Subnet'] = subnet.strip() + last_ip['Netmask'] = netmask.lstrip().rstrip(')') + else: - ret[iface][key.strip()] = val.strip() + current_iface[key] = val + return ret + def raw_interface_configs(): ''' Return raw configs for all interfaces From f3d35fb5c68478c43f1c7540ec00af35cf0d50e8 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Mon, 6 Feb 2017 12:20:08 -0700 Subject: [PATCH 02/14] Lint fixes --- salt/modules/win_ip.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index e06b3b3c747d..6b90952289bf 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -6,7 +6,6 @@ # Import python libs import logging -import socket import time # Import salt libs @@ -61,9 +60,9 @@ def _interface_configs(): if ':' not in line: if current_ip_list: - current_ip_list.append( line ) + current_ip_list.append(line) else: - log.warning( 'Cannot parse "{0}"'.format(line) ) + log.warning('Cannot parse "{0}"'.format(line)) continue key, val = line.split(':', 1) @@ -74,10 +73,10 @@ def _interface_configs(): if ('dns servers' in lkey) or ('wins servers' in lkey): current_ip_list = [] current_iface[key] = current_ip_list - current_ip_list.append( val ) + current_ip_list.append(val) elif 'ip address' in lkey: - current_iface.setdefault( 'ip_addrs', [] ).append( {key:val} ) + current_iface.setdefault('ip_addrs', []).append({key:val}) elif 'subnet prefix' in lkey: subnet, _, netmask = val.split(' ', 2) From c12990531005338f56213200bbadc7147400b7ee Mon Sep 17 00:00:00 2001 From: Raine Curtis Date: Fri, 3 Feb 2017 13:59:01 -0700 Subject: [PATCH 03/14] Added missing source_hash_name argument in get_managed function Additional fix to #33187 Customer was still seeing errors, this should now work. Tested with 2015.8.13 and 2016.11.2 --- salt/states/jboss7.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/states/jboss7.py b/salt/states/jboss7.py index 0272e96e4c0e..7eb6ad91c01b 100644 --- a/salt/states/jboss7.py +++ b/salt/states/jboss7.py @@ -412,6 +412,7 @@ def __get_artifact(salt_source): template=None, source=salt_source['source'], source_hash=None, + source_hash_name=None, user=None, group=None, mode=None, From 7bed68743e315d947c37c32f9e55b5d9337d5ad6 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Mon, 6 Feb 2017 14:42:30 -0700 Subject: [PATCH 04/14] [2016.3] Pylint fix (#39202) --- salt/modules/win_ip.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index 6b90952289bf..dc7416b78eb5 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -76,7 +76,7 @@ def _interface_configs(): current_ip_list.append(val) elif 'ip address' in lkey: - current_iface.setdefault('ip_addrs', []).append({key:val}) + current_iface.setdefault('ip_addrs', []).append({key: val}) elif 'subnet prefix' in lkey: subnet, _, netmask = val.split(' ', 2) @@ -90,7 +90,6 @@ def _interface_configs(): return ret - def raw_interface_configs(): ''' Return raw configs for all interfaces From be31e0559ca970aaab24fdb50c83ac6137ca5a2c Mon Sep 17 00:00:00 2001 From: Mike Place Date: Mon, 6 Feb 2017 16:25:48 -0700 Subject: [PATCH 05/14] Ignore empty dicts in highstate outputter Closes #37174 --- salt/output/highstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/output/highstate.py b/salt/output/highstate.py index 5e7a897874ce..e743c8af8df5 100644 --- a/salt/output/highstate.py +++ b/salt/output/highstate.py @@ -179,7 +179,7 @@ def _format_host(host, data): # Verify that the needed data is present data_tmp = {} for tname, info in six.iteritems(data): - if isinstance(info, dict) and '__run_num__' not in info: + if isinstance(info, dict) and info and '__run_num__' not in info: err = (u'The State execution failed to record the order ' 'in which all states were executed. The state ' 'return missing data is:') From e6dda4a625bb95e0e6c6170b5da299b003dd66c7 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Mon, 6 Feb 2017 21:30:13 -0600 Subject: [PATCH 06/14] Sort the return list from the fileserver.envs runner --- salt/runners/fileserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/runners/fileserver.py b/salt/runners/fileserver.py index 7c0383379e3e..b2761cdaf2b6 100644 --- a/salt/runners/fileserver.py +++ b/salt/runners/fileserver.py @@ -37,7 +37,7 @@ def envs(backend=None, sources=False, outputter=None): salt-run fileserver.envs git ''' fileserver = salt.fileserver.Fileserver(__opts__) - output = fileserver.envs(back=backend, sources=sources) + output = sorted(fileserver.envs(back=backend, sources=sources)) if outputter: salt.utils.warn_until( From c75066294668a7ea7bf3c429985fe31bc30f5165 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 13:50:41 -0600 Subject: [PATCH 07/14] Loader optimzation Use a str.join instead of str.format to make the full function name. --- salt/loader.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/salt/loader.py b/salt/loader.py index 9d3f5fec97fc..522f58a3992d 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -1416,8 +1416,12 @@ def _load_module(self, name): # It default's of course to the found callable attribute name # if no alias is defined. funcname = getattr(mod, '__func_alias__', {}).get(attr, attr) + try: + full_funcname = '.'.join((module_name, funcname)) + except TypeError: + full_funcname = '{0}.{1}'.format(module_name, funcname) # Save many references for lookups - self._dict['{0}.{1}'.format(module_name, funcname)] = func + self._dict[full_funcname] = func setattr(mod_dict, funcname, func) mod_dict[funcname] = func self._apply_outputter(func, mod) From afee047b082f18a4cd83c712d667ce1b818488f2 Mon Sep 17 00:00:00 2001 From: Daniel Wallace Date: Tue, 7 Feb 2017 14:27:01 -0600 Subject: [PATCH 08/14] default to utf8 encoding if not specified On errors, for some reason AWS does not always have an encoding on the message. Default to 'utf-8'. --- salt/utils/aws.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/utils/aws.py b/salt/utils/aws.py index a416eaaa02da..152e68f2020c 100644 --- a/salt/utils/aws.py +++ b/salt/utils/aws.py @@ -87,7 +87,7 @@ def creds(provider): proxies={'http': ''}, timeout=AWS_METADATA_TIMEOUT, ) result.raise_for_status() - role = result.text.encode(result.encoding) + role = result.text.encode(result.encoding or 'utf-8') except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError): return provider['id'], provider['key'], '' @@ -460,7 +460,7 @@ def query(params=None, setname=None, requesturl=None, location=None, ) LOG.trace( 'AWS Response Text: {0}'.format( - result.text.encode(result.encoding) + result.text.encode(result.encoding or 'utf-8') ) ) result.raise_for_status() @@ -501,7 +501,7 @@ def query(params=None, setname=None, requesturl=None, location=None, return {'error': data}, requesturl return {'error': data} - response = result.text.encode(result.encoding) + response = result.text.encode(result.encoding or 'utf-8') root = ET.fromstring(response) items = root[1] From ad1b1255f233e551cf75683b54c3b500232a261e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 15:03:11 -0600 Subject: [PATCH 09/14] Add clarification for jenkins execution module There are two modules on PyPI which import as "jenkins". Installing the wrong one will result in a traceback when the execution module tries to use the Python bindings. This commit adds information about the dependency to the docstring for the module. It also checks to make sure that jenkins.Jenkins is present in the __virtual__() func, and if not it will fail to load the execution module with a meaningful error message in the log. --- salt/modules/jenkins.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/salt/modules/jenkins.py b/salt/modules/jenkins.py index 07dfad0567cd..2b3999522aff 100644 --- a/salt/modules/jenkins.py +++ b/salt/modules/jenkins.py @@ -4,6 +4,11 @@ .. versionadded:: 2016.3.0 +:depends: python-jenkins_ Python module (not to be confused with jenkins_) + +.. _python-jenkins: https://pypi.python.org/pypi/python-jenkins +.. _jenkins: https://pypi.python.org/pypi/jenkins + :configuration: This module can be used by either passing an api key and version directly or by specifying both in a configuration profile in the salt master/minion config. @@ -45,9 +50,15 @@ def __virtual__(): :return: The virtual name of the module. ''' if HAS_JENKINS: - return __virtualname__ + if hasattr(jenkins, 'Jenkins'): + return __virtualname__ + else: + return (False, + 'The wrong Python module appears to be installed. Please ' + 'make sure that \'python-jenkins\' is installed, not ' + '\'jenkins\'.') return (False, 'The jenkins execution module cannot be loaded: ' - 'python jenkins library is not installed.') + 'python-jenkins is not installed.') def _connect(): From c88896c27707b01b28ec9d9f11a9dc98c5f1c195 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 7 Feb 2017 15:38:53 -0600 Subject: [PATCH 10/14] Avoid recursion in s3/svn ext_pillars This passes ``ext=False`` when compiling pillar data in these ext_pillar sources. --- salt/pillar/s3.py | 2 +- salt/pillar/svn_pillar.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/pillar/s3.py b/salt/pillar/s3.py index 6c5b9c724206..d821aa02a9aa 100644 --- a/salt/pillar/s3.py +++ b/salt/pillar/s3.py @@ -172,7 +172,7 @@ def ext_pillar(minion_id, pil = Pillar(opts, __grains__, minion_id, environment) - compiled_pillar = pil.compile_pillar() + compiled_pillar = pil.compile_pillar(ext=False) return compiled_pillar diff --git a/salt/pillar/svn_pillar.py b/salt/pillar/svn_pillar.py index 7be9ad9407e4..01f1fb0c1e9a 100644 --- a/salt/pillar/svn_pillar.py +++ b/salt/pillar/svn_pillar.py @@ -193,4 +193,4 @@ def ext_pillar(minion_id, opts = deepcopy(__opts__) opts['pillar_roots'][branch] = [pillar_dir] pil = Pillar(opts, __grains__, minion_id, branch) - return pil.compile_pillar() + return pil.compile_pillar(ext=False) From ef4e437bbc91df06f43d1a9c081be6dec131e126 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Tue, 7 Feb 2017 14:53:59 -0700 Subject: [PATCH 11/14] Fix the win_ip_test failures (#39230) The changes made in #38793 changes the "get_all_ointerfaces" call to be a list of DNS servers. This change adjusts the tests structure from a string to a list and fixes the test failure. --- tests/unit/modules/win_ip_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/modules/win_ip_test.py b/tests/unit/modules/win_ip_test.py index 301e8929b049..29c4b6a3a90a 100644 --- a/tests/unit/modules/win_ip_test.py +++ b/tests/unit/modules/win_ip_test.py @@ -65,12 +65,12 @@ def test_get_all_interfaces(self): Test if it return configs for all interfaces. ''' ret = {'Ethernet': {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]}} @@ -86,11 +86,11 @@ def test_get_interface(self): Test if it return the configuration of a network interface. ''' ret = {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': '1.2.3.4', + 'DNS servers configured through DHCP': ['1.2.3.4'], 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', 'InterfaceMetric': '20', 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': 'None', + 'WINS servers configured through DHCP': ['None'], 'ip_addrs': [{'IP Address': '1.2.3.74', 'Netmask': '255.255.255.0', 'Subnet': '1.2.3.0/24'}]} From c1d16cc3d05a10b7def4e0833409276bbc6c788e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 8 Feb 2017 15:52:45 -0600 Subject: [PATCH 12/14] Better handling of enabled/disabled arguments in pkgrepo.managed (#39251) This reverses the decision made in #35055 to deprecate the "enabled" parameter in this state. This was a poorly-conceived decision, likely made because the "enabled" param was not included in the docstring for the pkgrepo.managed state under the "YUM/ZYPPER" section, while the "disabled" param *was* listed under the "APT" section. "disabled" isn't a thing in yum/dnf, so there was never any reason to try to shoehorn it in. Not to mention the fact that the "disabled" argument isn't even supported in Zypper, which effectively breaks Zypper support. This commit normalizes enabled/disabled based on platform, so passing "enabled" in APT will pass the opposite value as "disabled", and vice-versa on the other platforms. This allows enabled/disabled to be used interchangeably. It also more gracefully handles booleans in yum/dnf/zypper, so that a bool can be properly compared to a 1/0 value. --- salt/modules/yumpkg.py | 19 ++++---- salt/states/pkgrepo.py | 101 ++++++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index a7dcdc0b870a..1bf4a87ed468 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -2265,18 +2265,12 @@ def mod_repo(repo, basedir=None, **kwargs): ) # Build a list of keys to be deleted - todelete = ['disabled'] + todelete = [] for key in repo_opts: if repo_opts[key] != 0 and not repo_opts[key]: del repo_opts[key] todelete.append(key) - # convert disabled to enabled respectively from pkgrepo state - if 'enabled' not in repo_opts: - repo_opts['enabled'] = int(str(repo_opts.pop('disabled', False)).lower() != 'true') - else: - repo_opts.pop('disabled', False) - # Add baseurl or mirrorlist to the 'todelete' list if the other was # specified in the repo_opts if 'mirrorlist' in repo_opts: @@ -2348,6 +2342,7 @@ def mod_repo(repo, basedir=None, **kwargs): if key in six.iterkeys(filerepos[repo].copy()): del filerepos[repo][key] + _bool_to_str = lambda x: '1' if x else '0' # Old file or new, write out the repos(s) filerepos[repo].update(repo_opts) content = header @@ -2358,7 +2353,12 @@ def mod_repo(repo, basedir=None, **kwargs): del filerepos[stanza]['comments'] content += '\n[{0}]'.format(stanza) for line in six.iterkeys(filerepos[stanza]): - content += '\n{0}={1}'.format(line, filerepos[stanza][line]) + content += '\n{0}={1}'.format( + line, + filerepos[stanza][line] + if not isinstance(filerepos[stanza][line], bool) + else _bool_to_str(filerepos[stanza][line]) + ) content += '\n{0}\n'.format(comments) with salt.utils.fopen(repofile, 'w') as fileout: @@ -2404,9 +2404,6 @@ def _parse_repo_file(filename): 'Failed to parse line in %s, offending line was ' '\'%s\'', filename, line.rstrip() ) - # YUM uses enabled field - create the disabled field so that comparisons works correctly in state - if comps[0].strip() == 'enabled': - repos[repo]['disabled'] = comps[1] != "1" return (header, repos) diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py index 7901c0b95541..229feda6f66c 100644 --- a/salt/states/pkgrepo.py +++ b/salt/states/pkgrepo.py @@ -112,7 +112,7 @@ def managed(name, ppa=None, **kwargs): `, :mod:`apt `, and :mod:`zypper ` repositories are supported. - **YUM OR ZYPPER-BASED SYSTEMS** + **YUM/DNF/ZYPPER-BASED SYSTEMS** .. note:: One of ``baseurl`` or ``mirrorlist`` below is required. Additionally, @@ -126,6 +126,16 @@ def managed(name, ppa=None, **kwargs): repo. Secondly, it will be the name of the file as stored in /etc/yum.repos.d (e.g. ``/etc/yum.repos.d/foo.conf``). + enabled : True + Whether or not the repo is enabled. Can be specified as True/False or + 1/0. + + disabled : False + Included to reduce confusion due to APT's use of the ``disabled`` + argument. If this is passed for a yum/dnf/zypper-based distro, then the + reverse will be passed as ``enabled``. For example passing + ``disabled=True`` will assume ``enabled=False``. + humanname This is used as the "name" value in the repo file in ``/etc/yum.repos.d/`` (or ``/etc/zypp/repos.d`` for Suse distros). @@ -203,10 +213,16 @@ def managed(name, ppa=None, **kwargs): 'deb http://us.archive.ubuntu.com/ubuntu precise main': pkgrepo.managed - disabled + disabled : False Toggles whether or not the repo is used for resolving dependencies and/or installing packages. + enabled : True + Included to reduce confusion due to yum/dnf/zypper's use of the + ``enabled`` argument. If this is passed for an APT-based distro, then + the reverse will be passed as ``disabled``. For example, passing + ``enabled=False`` will assume ``disabled=False``. + comps On apt-based systems, comps dictate the types of packages to be installed from the repository (e.g. main, nonfree, ...). For @@ -281,14 +297,19 @@ def managed(name, ppa=None, **kwargs): 'intended.') return ret - if 'enabled' in kwargs: - salt.utils.warn_until( - 'Nitrogen', - 'The `enabled` argument has been deprecated in favor of ' - '`disabled`.' - ) + enabled = kwargs.pop('enabled', None) + disabled = kwargs.pop('disabled', None) + + if enabled is not None and disabled is not None: + ret['result'] = False + ret['comment'] = 'Only one of enabled/disabled is allowed' + return ret + elif enabled is None and disabled is None: + # If neither argument was passed we assume the repo will be enabled + enabled = True repo = name + os_family = __grains__['os_family'].lower() if __grains__['os'] in ('Ubuntu', 'Mint'): if ppa is not None: # overload the name/repo value for PPAs cleanly @@ -298,26 +319,26 @@ def managed(name, ppa=None, **kwargs): except TypeError: repo = ':'.join(('ppa', str(ppa))) - elif __grains__['os_family'].lower() in ('redhat', 'suse'): + kwargs['disabled'] = not salt.utils.is_true(enabled) \ + if enabled is not None \ + else salt.utils.is_true(disabled) + + elif os_family in ('redhat', 'suse'): if 'humanname' in kwargs: kwargs['name'] = kwargs.pop('humanname') - _val = lambda x: '1' if salt.utils.is_true(x) else '0' - if 'disabled' in kwargs: - if 'enabled' in kwargs: - ret['result'] = False - ret['comment'] = 'Only one of enabled/disabled is permitted' - return ret - _reverse = lambda x: '1' if x == '0' else '0' - kwargs['enabled'] = _reverse(_val(kwargs.pop('disabled'))) - elif 'enabled' in kwargs: - kwargs['enabled'] = _val(kwargs['enabled']) if 'name' not in kwargs: # Fall back to the repo name if humanname not provided kwargs['name'] = repo - # Replace 'enabled' from kwargs with 'disabled' - enabled = kwargs.pop('enabled', True) - kwargs['disabled'] = not salt.utils.is_true(enabled) + kwargs['enabled'] = not salt.utils.is_true(disabled) \ + if disabled is not None \ + else salt.utils.is_true(enabled) + + elif os_family == 'nilinuxrt': + # opkg is the pkg virtual + kwargs['enabled'] = not salt.utils.is_true(disabled) \ + if disabled is not None \ + else salt.utils.is_true(enabled) for kwarg in _STATE_INTERNAL_KEYWORDS: kwargs.pop(kwarg, None) @@ -342,11 +363,10 @@ def managed(name, ppa=None, **kwargs): else: sanitizedkwargs = kwargs - if __grains__['os_family'] == 'Debian': + if os_family == 'debian': repo = _strip_uri(repo) if pre: - needs_update = False for kwarg in sanitizedkwargs: if kwarg not in pre: if kwarg == 'enabled': @@ -354,33 +374,40 @@ def managed(name, ppa=None, **kwargs): # not explicitly set, so we don't need to update the repo # if it's desired to be enabled and the 'enabled' key is # missing from the repo definition - if __grains__['os_family'] == 'RedHat': + if os_family == 'redhat': if not salt.utils.is_true(sanitizedkwargs[kwarg]): - needs_update = True + break else: - needs_update = True + break else: - needs_update = True + break elif kwarg == 'comps': if sorted(sanitizedkwargs[kwarg]) != sorted(pre[kwarg]): - needs_update = True - elif kwarg == 'line' and __grains__['os_family'] == 'Debian': + break + elif kwarg == 'line' and os_family == 'debian': # split the line and sort everything after the URL sanitizedsplit = sanitizedkwargs[kwarg].split() sanitizedsplit[3:] = sorted(sanitizedsplit[3:]) reposplit = pre[kwarg].split() reposplit[3:] = sorted(reposplit[3:]) if sanitizedsplit != reposplit: - needs_update = True + break if 'comments' in kwargs: _line = pre[kwarg].split('#') if str(kwargs['comments']) not in _line: - needs_update = True + break else: - if str(sanitizedkwargs[kwarg]) != str(pre[kwarg]): - needs_update = True - - if not needs_update: + if os_family in ('redhat', 'suse') \ + and any(isinstance(x, bool) for x in + (sanitizedkwargs[kwarg], pre[kwarg])): + # This check disambiguates 1/0 from True/False + if salt.utils.is_true(sanitizedkwargs[kwarg]) != \ + salt.utils.is_true(pre[kwarg]): + break + else: + if str(sanitizedkwargs[kwarg]) != str(pre[kwarg]): + break + else: ret['result'] = True ret['comment'] = ('Package repo \'{0}\' already configured' .format(name)) @@ -401,7 +428,7 @@ def managed(name, ppa=None, **kwargs): pass try: - if __grains__['os_family'] == 'Debian': + if os_family == 'debian': __salt__['pkg.mod_repo'](repo, saltenv=__env__, **kwargs) else: __salt__['pkg.mod_repo'](repo, **kwargs) From 1b9217d6362702d9fd49dfc09f0ada9051a11191 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 8 Feb 2017 17:51:56 -0600 Subject: [PATCH 13/14] Update jsonschema tests to reflect change in jsonschema 2.6.0 (#39260) Version 2.6.0 changed the wording of one of the exceptions tested, causing tests to fail when jsonschema 2.6.0 is installed. This commit updates the tests to change the assert for 2.6.0 and later. --- tests/unit/config/schemas/ssh_test.py | 17 +++++++++++++---- tests/unit/utils/schema_test.py | 18 ++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/unit/config/schemas/ssh_test.py b/tests/unit/config/schemas/ssh_test.py index 02b47ef7cc73..14f9bed3f617 100644 --- a/tests/unit/config/schemas/ssh_test.py +++ b/tests/unit/config/schemas/ssh_test.py @@ -7,6 +7,7 @@ ''' # Import python libs from __future__ import absolute_import, print_function +from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs from salttesting import TestCase, skipIf @@ -23,8 +24,10 @@ import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True + JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False + JSONSCHEMA_VERSION = _LooseVersion('0') class RoosterEntryConfigTest(TestCase): @@ -296,7 +299,13 @@ def test_roster_config_validate(self): ssh_schemas.RosterItem.serialize(), format_checker=jsonschema.FormatChecker() ) - self.assertIn( - 'Additional properties are not allowed (\'target-1:1\' was unexpected)', - excinfo.exception.message - ) + if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + self.assertIn( + 'Additional properties are not allowed (\'target-1:1\' was unexpected)', + excinfo.exception.message + ) + else: + self.assertIn( + '\'target-1:1\' does not match any of the regexes', + excinfo.exception.message + ) diff --git a/tests/unit/utils/schema_test.py b/tests/unit/utils/schema_test.py index 73db06d7fd23..e4f50cf73cf4 100644 --- a/tests/unit/utils/schema_test.py +++ b/tests/unit/utils/schema_test.py @@ -6,7 +6,7 @@ # Import python libs from __future__ import absolute_import -from distutils.version import LooseVersion +from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs from salttesting import TestCase, skipIf @@ -22,10 +22,10 @@ import jsonschema import jsonschema.exceptions HAS_JSONSCHEMA = True - JSONSCHEMA_VERSION = jsonschema.__version__ + JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: - JSONSCHEMA_VERSION = '' HAS_JSONSCHEMA = False + JSONSCHEMA_VERSION = _LooseVersion('0') # pylint: disable=unused-import @@ -749,8 +749,7 @@ def test_ipv4_config(self): } ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - @skipIf(HAS_JSONSCHEMA and LooseVersion(jsonschema.__version__) <= LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') + @skipIf(JSONSCHEMA_VERSION <= _LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') def test_ipv4_config_validation(self): class TestConf(schema.Schema): item = schema.IPv4Item(title='Item', description='Item description') @@ -1702,7 +1701,14 @@ class TestConf(schema.Schema): with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'color': 'green', 'sides': 4, 'surfaces': 4}}, TestConf.serialize()) - self.assertIn('Additional properties are not allowed', excinfo.exception.message) + if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + self.assertIn( + 'Additional properties are not allowed', + excinfo.exception.message) + else: + self.assertIn( + '\'surfaces\' does not match any of the regexes', + excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( From a9c2c106c18d375087589e6f658b2be35e6e9d38 Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 9 Feb 2017 08:50:56 -0700 Subject: [PATCH 14/14] Pylint fix --- tests/unit/utils/schema_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/utils/schema_test.py b/tests/unit/utils/schema_test.py index 8cbeae3df741..3bc42edb1412 100644 --- a/tests/unit/utils/schema_test.py +++ b/tests/unit/utils/schema_test.py @@ -9,8 +9,6 @@ import json import yaml -from distutils.version import LooseVersion - from distutils.version import LooseVersion as _LooseVersion # Import Salt Testing Libs