Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
systemsmanagement:Uyuni:Master:CentOS6-Uyuni-Client-Tools
salt
add-all_versions-parameter-to-include-all-insta...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File add-all_versions-parameter-to-include-all-installed-.patch of Package salt
From da52c7f9b00f85f091e8eee95d93b2c290306ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernandez@suse.com> Date: Mon, 14 May 2018 11:33:13 +0100 Subject: [PATCH] Add "all_versions" parameter to include all installed version on rpm.info Enable "all_versions" parameter for zypper.info_installed Enable "all_versions" parameter for yumpkg.info_installed Prevent adding failed packages when pkg name contains the arch (on SUSE) Add 'all_versions' documentation for info_installed on yum/zypper modules Add unit tests for info_installed with all_versions Allow removing only specific package versions with zypper and yum Refactor: use dict.setdefault instead if-else statement --- salt/modules/rpm.py | 18 +++++++-- salt/modules/yumpkg.py | 49 ++++++++++++++++++------ salt/modules/zypper.py | 80 +++++++++++++++++++++++++++++---------- salt/states/pkg.py | 33 +++++++++++++++- tests/unit/modules/test_yumpkg.py | 50 ++++++++++++++++++++++++ tests/unit/modules/zypper_test.py | 50 ++++++++++++++++++++++++ 6 files changed, 244 insertions(+), 36 deletions(-) diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py index 9dace3cb6f..0874101d76 100644 --- a/salt/modules/rpm.py +++ b/salt/modules/rpm.py @@ -448,7 +448,7 @@ def diff(package, path): return res -def info(*packages, **attr): +def info(*packages, **kwargs): ''' Return a detailed package(s) summary information. If no packages specified, all packages will be returned. @@ -462,6 +462,9 @@ def info(*packages, **attr): version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t, build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description. + :param all_versions: + Return information for all installed versions of the packages + :return: CLI example: @@ -471,7 +474,9 @@ def info(*packages, **attr): salt '*' lowpkg.info apache2 bash salt '*' lowpkg.info apache2 bash attr=version salt '*' lowpkg.info apache2 bash attr=version,build_date_iso,size + salt '*' lowpkg.info apache2 bash attr=version,build_date_iso,size all_versions=True ''' + all_versions = kwargs.get('all_versions', False) # LONGSIZE is not a valid tag for all versions of rpm. If LONGSIZE isn't # available, then we can just use SIZE for older versions. See Issue #31366. rpm_tags = __salt__['cmd.run_stdout']( @@ -511,7 +516,7 @@ def info(*packages, **attr): "edition": "edition: %|EPOCH?{%{EPOCH}:}|%{VERSION}-%{RELEASE}\\n", } - attr = attr.get('attr', None) and attr['attr'].split(",") or None + attr = kwargs.get('attr', None) and kwargs['attr'].split(",") or None query = list() if attr: for attr_k in attr: @@ -605,8 +610,13 @@ def info(*packages, **attr): if pkg_name.startswith('gpg-pubkey'): continue if pkg_name not in ret: - ret[pkg_name] = pkg_data.copy() - del ret[pkg_name]['edition'] + if all_versions: + ret[pkg_name] = [pkg_data.copy()] + else: + ret[pkg_name] = pkg_data.copy() + del ret[pkg_name]['edition'] + elif all_versions: + ret[pkg_name].append(pkg_data.copy()) return ret diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index 8a0d22e062..b88c4e1f13 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -867,31 +867,39 @@ def list_downloaded(): return ret -def info_installed(*names): +def info_installed(*names, **kwargs): ''' .. versionadded:: 2015.8.1 Return the information of the named package(s), installed on the system. + :param all_versions: + Include information for all versions of the packages installed on the minion. + CLI example: .. code-block:: bash salt '*' pkg.info_installed <package1> salt '*' pkg.info_installed <package1> <package2> <package3> ... + salt '*' pkg.info_installed <package1> <package2> <package3> all_versions=True ''' + all_versions = kwargs.get('all_versions', False) ret = dict() - for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names).items(): - t_nfo = dict() - # Translate dpkg-specific keys to a common structure - for key, value in pkg_nfo.items(): - if key == 'source_rpm': - t_nfo['source'] = value + for pkg_name, pkgs_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): + pkg_nfo = pkgs_nfo if all_versions else [pkgs_nfo] + for _nfo in pkg_nfo: + t_nfo = dict() + # Translate dpkg-specific keys to a common structure + for key, value in _nfo.items(): + if key == 'source_rpm': + t_nfo['source'] = value + else: + t_nfo[key] = value + if not all_versions: + ret[pkg_name] = t_nfo else: - t_nfo[key] = value - - ret[pkg_name] = t_nfo - + ret.setdefault(pkg_name, []).append(t_nfo) return ret @@ -1707,7 +1715,24 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 raise CommandExecutionError(exc) old = list_pkgs() - targets = [x for x in pkg_params if x in old] + targets = [] + for target in pkg_params: + # Check if package version set to be removed is actually installed: + # old[target] contains a comma-separated list of installed versions + if target in old and not pkg_params[target]: + targets.append(target) + elif target in old and pkg_params[target] in old[target].split(','): + arch = '' + pkgname = target + try: + namepart, archpart = target.rsplit('.', 1) + except ValueError: + pass + else: + if archpart in salt.utils.pkg.rpm.ARCHES: + arch = '.' + archpart + pkgname = namepart + targets.append('{0}-{1}{2}'.format(pkgname, pkg_params[target], arch)) if not targets: return {} diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index 77cb9d9fbb..cce2ffd895 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -373,37 +373,46 @@ def info_installed(*names, **kwargs): Valid attributes are: ignore, report + :param all_versions: + Include information for all versions of the packages installed on the minion. + CLI example: .. code-block:: bash salt '*' pkg.info_installed <package1> salt '*' pkg.info_installed <package1> <package2> <package3> ... - salt '*' pkg.info_installed <package1> attr=version,vendor + salt '*' pkg.info_installed <package1> <package2> <package3> all_versions=True + salt '*' pkg.info_installed <package1> attr=version,vendor all_versions=True salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor errors=ignore salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor errors=report ''' + all_versions = kwargs.get('all_versions', False) ret = dict() - for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): - t_nfo = dict() - # Translate dpkg-specific keys to a common structure - for key, value in pkg_nfo.items(): - if isinstance(value, six.string_types): - # Check, if string is encoded in a proper UTF-8 - if six.PY3: - value_ = value.encode('UTF-8', 'ignore').decode('UTF-8', 'ignore') + for pkg_name, pkgs_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): + pkg_nfo = pkgs_nfo if all_versions else [pkgs_nfo] + for _nfo in pkg_nfo: + t_nfo = dict() + # Translate dpkg-specific keys to a common structure + for key, value in _nfo.items(): + if isinstance(value, six.string_types): + # Check, if string is encoded in a proper UTF-8 + if six.PY3: + value_ = value.encode('UTF-8', 'ignore').decode('UTF-8', 'ignore') + else: + value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore') + if value != value_: + value = kwargs.get('errors', 'ignore') == 'ignore' and value_ or 'N/A (invalid UTF-8)' + log.error('Package {0} has bad UTF-8 code in {1}: {2}'.format(pkg_name, key, value)) + if key == 'source_rpm': + t_nfo['source'] = value else: - value_ = value.decode('UTF-8', 'ignore').encode('UTF-8', 'ignore') - if value != value_: - value = kwargs.get('errors', 'ignore') == 'ignore' and value_ or 'N/A (invalid UTF-8)' - log.error('Package {0} has bad UTF-8 code in {1}: {2}'.format(pkg_name, key, value)) - if key == 'source_rpm': - t_nfo['source'] = value + t_nfo[key] = value + if not all_versions: + ret[pkg_name] = t_nfo else: - t_nfo[key] = value - ret[pkg_name] = t_nfo - + ret.setdefault(pkg_name, []).append(t_nfo) return ret @@ -1271,7 +1280,14 @@ def _uninstall(name=None, pkgs=None): raise CommandExecutionError(exc) old = list_pkgs() - targets = [target for target in pkg_params if target in old] + targets = [] + for target in pkg_params: + # Check if package version set to be removed is actually installed: + # old[target] contains a comma-separated list of installed versions + if target in old and pkg_params[target] in old[target].split(','): + targets.append(target + "-" + pkg_params[target]) + elif target in old and not pkg_params[target]: + targets.append(target) if not targets: return {} @@ -1294,6 +1310,32 @@ def _uninstall(name=None, pkgs=None): return ret +def normalize_name(name): + ''' + Strips the architecture from the specified package name, if necessary. + Circumstances where this would be done include: + + * If the arch is 32 bit and the package name ends in a 32-bit arch. + * If the arch matches the OS arch, or is ``noarch``. + + CLI Example: + + .. code-block:: bash + + salt '*' pkg.normalize_name zsh.x86_64 + ''' + try: + arch = name.rsplit('.', 1)[-1] + if arch not in salt.utils.pkg.rpm.ARCHES + ('noarch',): + return name + except ValueError: + return name + if arch in (__grains__['osarch'], 'noarch') \ + or salt.utils.pkg.rpm.check_32(arch, osarch=__grains__['osarch']): + return name[:-(len(arch) + 1)] + return name + + def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument ''' .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 diff --git a/salt/states/pkg.py b/salt/states/pkg.py index db67ade08e..1aadae80cb 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -417,6 +417,16 @@ def _find_remove_targets(name=None, if __grains__['os'] == 'FreeBSD' and origin: cver = [k for k, v in six.iteritems(cur_pkgs) if v['origin'] == pkgname] + elif __grains__['os_family'] == 'Suse': + # On SUSE systems. Zypper returns packages without "arch" in name + try: + namepart, archpart = pkgname.rsplit('.', 1) + except ValueError: + cver = cur_pkgs.get(pkgname, []) + else: + if archpart in salt.utils.pkg.rpm.ARCHES + ("noarch",): + pkgname = namepart + cver = cur_pkgs.get(pkgname, []) else: cver = cur_pkgs.get(pkgname, []) @@ -767,6 +777,17 @@ def _verify_install(desired, new_pkgs, ignore_epoch=False): cver = [k for k, v in six.iteritems(new_pkgs) if v['origin'] == pkgname] elif __grains__['os_family'] == 'Debian': cver = new_pkgs.get(pkgname.split('=')[0]) + elif __grains__['os_family'] == 'Suse': + # On SUSE systems. Zypper returns packages without "arch" in name + try: + namepart, archpart = pkgname.rsplit('.', 1) + except ValueError: + cver = new_pkgs.get(pkgname) + else: + if archpart in salt.utils.pkg.rpm.ARCHES + ("noarch",): + cver = new_pkgs.get(namepart) + else: + cver = new_pkgs.get(pkgname) else: cver = new_pkgs.get(pkgname) @@ -2545,7 +2566,17 @@ def _uninstall( changes = __salt__['pkg.{0}'.format(action)](name, pkgs=pkgs, version=version, **kwargs) new = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) - failed = [x for x in pkg_params if x in new] + failed = [] + for x in pkg_params: + if __grains__['os_family'] in ['Suse', 'RedHat']: + # Check if the package version set to be removed is actually removed: + if x in new and not pkg_params[x]: + failed.append(x) + elif x in new and pkg_params[x] in new[x]: + failed.append(x + "-" + pkg_params[x]) + elif x in new: + failed.append(x) + if action == 'purge': new_removed = __salt__['pkg.list_pkgs'](versions_as_list=True, removed=True, diff --git a/tests/unit/modules/test_yumpkg.py b/tests/unit/modules/test_yumpkg.py index ae3b29c472..40fc3e95ed 100644 --- a/tests/unit/modules/test_yumpkg.py +++ b/tests/unit/modules/test_yumpkg.py @@ -206,3 +206,53 @@ class YumTestCase(TestCase): }}.items(): self.assertTrue(pkgs.get(pkg_name)) self.assertEqual(pkgs[pkg_name], [pkg_attr]) + + def test_info_installed_with_all_versions(self): + ''' + Test the return information of all versions for the named package(s), installed on the system. + + :return: + ''' + run_out = { + 'virgo-dummy': [ + {'build_date': '2015-07-09T10:55:19Z', + 'vendor': 'openSUSE Build Service', + 'description': 'This is the Virgo dummy package used for testing SUSE Manager', + 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', + 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', + 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', + 'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0', + 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', + 'release': '1.1', 'group': 'Applications/System', 'arch': 'i686', 'size': '17992'}, + {'build_date': '2015-07-09T10:15:19Z', + 'vendor': 'openSUSE Build Service', + 'description': 'This is the Virgo dummy package used for testing SUSE Manager', + 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', + 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', + 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', + 'install_date_time_t': 14562415127, 'summary': 'Virgo dummy package', 'version': '1.0', + 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', + 'release': '1.1', 'group': 'Applications/System', 'arch': 'x86_64', 'size': '13124'} + ], + 'libopenssl1_0_0': [ + {'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>', + 'description': 'The OpenSSL Project is a collaborative effort.', + 'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/', + 'build_date_time_t': 1446675634, 'relocations': '(not relocatable)', + 'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z', + 'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security', + 'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82', + 'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/', + 'arch': 'x86_64', 'size': '2576912'} + ] + } + with patch.dict(yumpkg.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + installed = yumpkg.info_installed(all_versions=True) + # Test overall products length + self.assertEqual(len(installed), 2) + + # Test multiple versions for the same package + for pkg_name, pkg_info_list in installed.items(): + self.assertEqual(len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1) + for info in pkg_info_list: + self.assertTrue(info['arch'] in ('x86_64', 'i686')) diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py index c82dedec29..a0d136a9c1 100644 --- a/tests/unit/modules/zypper_test.py +++ b/tests/unit/modules/zypper_test.py @@ -324,6 +324,56 @@ class ZypperTestCase(TestCase): installed = zypper.info_installed() self.assertEqual(installed['vīrgô']['description'], 'vīrgô d€šçripţiǫñ') + def test_info_installed_with_all_versions(self): + ''' + Test the return information of all versions for the named package(s), installed on the system. + + :return: + ''' + run_out = { + 'virgo-dummy': [ + {'build_date': '2015-07-09T10:55:19Z', + 'vendor': 'openSUSE Build Service', + 'description': 'This is the Virgo dummy package used for testing SUSE Manager', + 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', + 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', + 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', + 'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0', + 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', + 'release': '1.1', 'group': 'Applications/System', 'arch': 'i686', 'size': '17992'}, + {'build_date': '2015-07-09T10:15:19Z', + 'vendor': 'openSUSE Build Service', + 'description': 'This is the Virgo dummy package used for testing SUSE Manager', + 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', + 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', + 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', + 'install_date_time_t': 14562415127, 'summary': 'Virgo dummy package', 'version': '1.0', + 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', + 'release': '1.1', 'group': 'Applications/System', 'arch': 'x86_64', 'size': '13124'} + ], + 'libopenssl1_0_0': [ + {'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>', + 'description': 'The OpenSSL Project is a collaborative effort.', + 'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/', + 'build_date_time_t': 1446675634, 'relocations': '(not relocatable)', + 'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z', + 'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security', + 'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82', + 'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/', + 'arch': 'x86_64', 'size': '2576912'} + ] + } + with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + installed = zypper.info_installed(all_versions=True) + # Test overall products length + self.assertEqual(len(installed), 2) + + # Test multiple versions for the same package + for pkg_name, pkg_info_list in installed.items(): + self.assertEqual(len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1) + for info in pkg_info_list: + self.assertTrue(info['arch'] in ('x86_64', 'i686')) + def test_info_available(self): ''' Test return the information of the named package available for the system. -- 2.15.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor