Accepting request 365937 from systemsmanagement:saltstack

1

OBS-URL: https://build.opensuse.org/request/show/365937
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/salt?expand=0&rev=56
This commit is contained in:
Dominique Leuenberger 2016-03-07 12:27:38 +00:00 committed by Git OBS Bridge
parent 1592ccc37e
commit ac8e053af9
8 changed files with 1387 additions and 0 deletions

View File

@ -0,0 +1,673 @@
From 5ee519e885134c1afa77d9e78c53224ad70a2e51 Mon Sep 17 00:00:00 2001
From: Bo Maryniuk <bo@suse.de>
Date: Tue, 23 Feb 2016 17:34:37 +0100
Subject: [PATCH 23/23] Initial Zypper Unit Tests and bugfixes
Add Zypper Unit Test installed products sample data
Add Zypper unit test: test_list_products and test_refresh_db
Reimplement list_upgrades to use XML output from Zypper instead
Rename Zypper products static test data file
Use renamed zypper products data file
Do not strip the output
Implement error handling test for listing upgrades
Add list upgrades Zypper static data
Implement list upgrades test
Use strings instead of unicode strings
Implement test for info_installed
Add Zypper static data for the available packages
Implement test for the info_available
Implement test for latest_available
Bugfix: when only one package, no dict is returned. Still upgrade_available should return boolean.
Implement test for the upgrade_available
Add third test package static info
Adjust test case for the third package in the test static data
Implement test for version compare, where RPM algorithm is called
Implement test for version compare, where python fall-back algorithm is called
Add mocking data
Implement list packages test
Add space before "assert" keyword
Fix PyLint
Do not use Zypper purge (reason: too dangerous)
Fix the docstring
Refactor code (a bit)
Implement unit test for remove and purge
---
salt/modules/zypper.py | 62 ++---
tests/unit/modules/zypp/zypper-available.txt | 64 ++++++
tests/unit/modules/zypp/zypper-products.xml | 37 +++
tests/unit/modules/zypp/zypper-updates.xml | 33 +++
tests/unit/modules/zypper_test.py | 324 +++++++++++++++++++++++++++
5 files changed, 482 insertions(+), 38 deletions(-)
create mode 100644 tests/unit/modules/zypp/zypper-available.txt
create mode 100644 tests/unit/modules/zypp/zypper-products.xml
create mode 100644 tests/unit/modules/zypp/zypper-updates.xml
create mode 100644 tests/unit/modules/zypper_test.py
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
index 33e5da9..ab8bb06 100644
--- a/salt/modules/zypper.py
+++ b/salt/modules/zypper.py
@@ -87,34 +87,21 @@ def list_upgrades(refresh=True):
'''
if refresh:
refresh_db()
- ret = {}
- call = __salt__['cmd.run_all'](
- _zypper('list-updates'), output_loglevel='trace'
- )
- if call['retcode'] != 0:
- comment = ''
- if 'stderr' in call:
- comment += call['stderr']
- if 'stdout' in call:
- comment += call['stdout']
- raise CommandExecutionError(
- '{0}'.format(comment)
- )
- else:
- out = call['stdout']
+ ret = dict()
+ run_data = __salt__['cmd.run_all'](_zypper('-x', 'list-updates'), output_loglevel='trace')
+ if run_data['retcode'] != 0:
+ msg = list()
+ for chnl in ['stderr', 'stdout']:
+ if run_data.get(chnl, ''):
+ msg.append(run_data[chnl])
+ raise CommandExecutionError(os.linesep.join(msg) or
+ 'Zypper returned non-zero system exit. See Zypper logs for more details.')
+
+ doc = dom.parseString(run_data['stdout'])
+ for update_node in doc.getElementsByTagName('update'):
+ if update_node.getAttribute('kind') == 'package':
+ ret[update_node.getAttribute('name')] = update_node.getAttribute('edition')
- for line in out.splitlines():
- if not line:
- continue
- if '|' not in line:
- continue
- try:
- status, repo, name, cur, avail, arch = \
- [x.strip() for x in line.split('|')]
- except (ValueError, IndexError):
- continue
- if status == 'v':
- ret[name] = avail
return ret
# Provide a list_updates function for those used to using zypper list-updates
@@ -300,7 +287,7 @@ def upgrade_available(name):
salt '*' pkg.upgrade_available <package name>
'''
- return latest_version(name).get(name) is not None
+ return not not latest_version(name)
def version(*names, **kwargs):
@@ -903,9 +890,9 @@ def upgrade(refresh=True):
return ret
-def _uninstall(action='remove', name=None, pkgs=None):
+def _uninstall(name=None, pkgs=None):
'''
- remove and purge do identical things but with different zypper commands,
+ Remove and purge do identical things but with different Zypper commands,
this function performs the common logic.
'''
try:
@@ -913,18 +900,17 @@ def _uninstall(action='remove', name=None, pkgs=None):
except MinionError as exc:
raise CommandExecutionError(exc)
- purge_arg = '-u' if action == 'purge' else ''
old = list_pkgs()
- targets = [x for x in pkg_params if x in old]
+ targets = [target for target in pkg_params if target in old]
if not targets:
return {}
+
while targets:
- cmd = _zypper('remove', purge_arg, *targets[:500])
- __salt__['cmd.run'](cmd, output_loglevel='trace')
+ __salt__['cmd.run'](_zypper('remove', *targets[:500]), output_loglevel='trace')
targets = targets[500:]
__context__.pop('pkg.list_pkgs', None)
- new = list_pkgs()
- return salt.utils.compare_dicts(old, new)
+
+ return salt.utils.compare_dicts(old, list_pkgs())
def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
@@ -954,7 +940,7 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.remove <package1>,<package2>,<package3>
salt '*' pkg.remove pkgs='["foo", "bar"]'
'''
- return _uninstall(action='remove', name=name, pkgs=pkgs)
+ return _uninstall(name=name, pkgs=pkgs)
def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
@@ -985,7 +971,7 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.purge <package1>,<package2>,<package3>
salt '*' pkg.purge pkgs='["foo", "bar"]'
'''
- return _uninstall(action='purge', name=name, pkgs=pkgs)
+ return _uninstall(name=name, pkgs=pkgs)
def list_locks():
diff --git a/tests/unit/modules/zypp/zypper-available.txt b/tests/unit/modules/zypp/zypper-available.txt
new file mode 100644
index 0000000..e1094bc
--- /dev/null
+++ b/tests/unit/modules/zypp/zypper-available.txt
@@ -0,0 +1,64 @@
+Loading repository data...
+Reading installed packages...
+
+
+Information for package vim:
+----------------------------
+Repository: SLE12-SP1-x86_64-Pool
+Name: vim
+Version: 7.4.326-2.62
+Arch: x86_64
+Vendor: SUSE LLC <https://www.suse.com/>
+Support Level: Level 3
+Installed: No
+Status: not installed
+Installed Size: 2,6 MiB
+Summary: Vi IMproved
+Description:
+ Vim (Vi IMproved) is an almost compatible version of the UNIX editor
+ vi. Almost every possible command can be performed using only ASCII
+ characters. Only the 'Q' command is missing (you do not need it). Many
+ new features have been added: multilevel undo, command line history,
+ file name completion, block operations, and editing of binary data.
+
+ Vi is available for the AMIGA, MS-DOS, Windows NT, and various versions
+ of UNIX.
+
+ For SUSE Linux, Vim is used as /usr/bin/vi.
+
+Information for package python:
+-------------------------------
+Repository: SLE12-SP1-x86_64-Pool
+Name: python
+Version: 2.7.9-20.2
+Arch: x86_64
+Vendor: SUSE LLC <https://www.suse.com/>
+Support Level: Level 3
+Installed: Yes
+Status: up-to-date
+Installed Size: 1,4 MiB
+Summary: Python Interpreter
+Description:
+ Python is an interpreted, object-oriented programming language, and is
+ often compared to Tcl, Perl, Scheme, or Java. You can find an overview
+ of Python in the documentation and tutorials included in the python-doc
+ (HTML) or python-doc-pdf (PDF) packages.
+
+ If you want to install third party modules using distutils, you need to
+ install python-devel package.
+
+Information for package emacs:
+------------------------------
+Repository: SLE12-SP1-x86_64-Pool
+Name: emacs
+Version: 24.3-14.44
+Arch: x86_64
+Vendor: SUSE LLC <https://www.suse.com/>
+Support Level: Level 3
+Installed: Yes
+Status: up-to-date
+Installed Size: 63,9 MiB
+Summary: GNU Emacs Base Package
+Description:
+ Basic package for the GNU Emacs editor. Requires emacs-x11 or
+ emacs-nox.
diff --git a/tests/unit/modules/zypp/zypper-products.xml b/tests/unit/modules/zypp/zypper-products.xml
new file mode 100644
index 0000000..1a50363
--- /dev/null
+++ b/tests/unit/modules/zypp/zypper-products.xml
@@ -0,0 +1,37 @@
+<?xml version='1.0'?>
+<stream>
+<message type="info">Loading repository data...</message>
+<message type="info">Reading installed packages...</message>
+<product-list>
+<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE LLC &lt;https://www.suse.com/&gt;" summary="SUSE Linux Enterprise Server 12 SP1" repo="SLE12-SP1-x86_64-Pool" productline="" registerrelease="" shortname="SLES12-SP1" flavor="POOL" isbase="false" installed="false"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
+ suite of products built on a single code base.
+ The platform addresses business needs from
+ the smallest thin-client devices to the world&apos;s
+ most powerful high-performance computing
+ and mainframe servers. SUSE Linux Enterprise
+ offers common management tools and technology
+ certifications across the platform, and
+ each product is enterprise-class.</description></product>
+<product name="SUSE-Manager-Proxy" version="3.0" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Proxy" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="SUSE Manager Proxy" flavor="DVD" isbase="false" installed="false"><endoflife time_t="1522454400" text="2018-03-31T02:00:00+02"/><registerflavor>extension</registerflavor><description>SUSE Manager Proxies extend large and/or geographically
+dispersed SUSE Manager environments to reduce load on the SUSE Manager
+Server, lower bandwidth needs, and provide faster local
+updates.</description></product>
+<product name="SUSE-Manager-Server" version="3.0" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Server" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="SUSE Manager Server" flavor="DVD" isbase="false" installed="false"><endoflife time_t="1522454400" text="2018-03-31T02:00:00+02"/><registerflavor>extension</registerflavor><description>SUSE Manager lets you efficiently manage physical, virtual,
+and cloud-based Linux systems. It provides automated and cost-effective
+configuration and software management, asset management, and system
+provisioning.</description></product>
+<product name="sle-manager-tools-beta" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><endoflife time_t="1509408000" text="2017-10-31T01:00:00+01"/><registerflavor>extension</registerflavor><description>&lt;p&gt;
+ SUSE Manager Tools provide packages required to connect to a
+ SUSE Manager Server.
+ &lt;p&gt;</description></product>
+<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE" summary="SUSE Linux Enterprise Server 12 SP1" repo="@System" productline="sles" registerrelease="" shortname="SLES12-SP1" flavor="DVD" isbase="true" installed="true"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
+ suite of products built on a single code base.
+ The platform addresses business needs from
+ the smallest thin-client devices to the world&apos;s
+ most powerful high-performance computing
+ and mainframe servers. SUSE Linux Enterprise
+ offers common management tools and technology
+ certifications across the platform, and
+ each product is enterprise-class.</description></product>
+</product-list>
+</stream>
diff --git a/tests/unit/modules/zypp/zypper-updates.xml b/tests/unit/modules/zypp/zypper-updates.xml
new file mode 100644
index 0000000..61fe85b
--- /dev/null
+++ b/tests/unit/modules/zypp/zypper-updates.xml
@@ -0,0 +1,33 @@
+<?xml version='1.0'?>
+<stream>
+<message type="info">Loading repository data...</message>
+<message type="info">Reading installed packages...</message>
+<update-status version="0.6">
+<update-list>
+ <update name="SUSEConnect" edition="0.2.33-7.1" arch="x86_64" kind="package" >
+ <summary>Utility to register a system with the SUSE Customer Center </summary>
+ <description>This package provides a command line tool and rubygem library for connecting a
+client system to the SUSE Customer Center. It will connect the system to your
+product subscriptions and enable the product repositories/services locally.</description>
+ <license></license>
+ <source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
+ </update>
+ <update name="bind-libs" edition="9.9.6P1-35.1" arch="x86_64" kind="package" >
+ <summary>Shared libraries of BIND </summary>
+ <description>This package contains the shared libraries of the Berkeley Internet
+Name Domain (BIND) Domain Name System implementation of the Domain Name
+System (DNS) protocols.</description>
+ <license></license>
+ <source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
+ </update>
+ <update name="bind-utils" edition="9.9.6P1-35.1" arch="x86_64" kind="package" >
+ <summary>Utilities to query and test DNS </summary>
+ <description>This package includes the utilities host, dig, and nslookup used to
+test and query the Domain Name System (DNS). The Berkeley Internet
+Name Domain (BIND) DNS server is found in the package named bind.</description>
+ <license></license>
+ <source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
+ </update>
+</update-list>
+</update-status>
+</stream>
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
new file mode 100644
index 0000000..de964f9
--- /dev/null
+++ b/tests/unit/modules/zypper_test.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+'''
+ :codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
+'''
+
+# Import Python Libs
+from __future__ import absolute_import
+
+# Import Salt Testing Libs
+from salttesting import TestCase, skipIf
+from salttesting.mock import (
+ MagicMock,
+ patch,
+ NO_MOCK,
+ NO_MOCK_REASON
+)
+from salt.exceptions import CommandExecutionError
+
+import os
+
+from salttesting.helpers import ensure_in_syspath
+
+ensure_in_syspath('../../')
+
+
+def get_test_data(filename):
+ '''
+ Return static test data
+ '''
+ return open(os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'zypp'), filename)).read()
+
+
+# Import Salt Libs
+from salt.modules import zypper
+
+# Globals
+zypper.__salt__ = dict()
+zypper.__context__ = dict()
+zypper.rpm = None
+
+
+@skipIf(NO_MOCK, NO_MOCK_REASON)
+class ZypperTestCase(TestCase):
+ '''
+ Test cases for salt.modules.zypper
+ '''
+
+ def test_list_upgrades(self):
+ '''
+ List package upgrades
+ :return:
+ '''
+ ref_out = {
+ 'stdout': get_test_data('zypper-updates.xml'),
+ 'stderr': None,
+ 'retcode': 0
+ }
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
+ upgrades = zypper.list_upgrades(refresh=False)
+ assert len(upgrades) == 3
+ for pkg, version in {'SUSEConnect': '0.2.33-7.1',
+ 'bind-utils': '9.9.6P1-35.1',
+ 'bind-libs': '9.9.6P1-35.1'}.items():
+ assert pkg in upgrades
+ assert upgrades[pkg] == version
+
+ def test_list_upgrades_error_handling(self):
+ '''
+ Test error handling in the list package upgrades.
+ :return:
+ '''
+ # Test handled errors
+ ref_out = {
+ 'stderr': 'Some handled zypper internal error',
+ 'retcode': 1
+ }
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
+ try:
+ zypper.list_upgrades(refresh=False)
+ except CommandExecutionError as error:
+ assert error.message == ref_out['stderr']
+
+ # Test unhandled error
+ ref_out = {
+ 'retcode': 1
+ }
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
+ try:
+ zypper.list_upgrades(refresh=False)
+ except CommandExecutionError as error:
+ assert error.message == 'Zypper returned non-zero system exit. See Zypper logs for more details.'
+
+ def test_list_products(self):
+ '''
+ List products test.
+ '''
+ ref_out = get_test_data('zypper-products.xml')
+ with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=ref_out)}):
+ products = zypper.list_products()
+ assert len(products) == 5
+ assert (['SLES', 'SLES', 'SUSE-Manager-Proxy', 'SUSE-Manager-Server', 'sle-manager-tools-beta'] ==
+ sorted([prod['name'] for prod in products]))
+ assert ('SUSE LLC <https://www.suse.com/>' in [product['vendor'] for product in products])
+ assert ([False, False, False, False, True] ==
+ sorted([product['isbase'] for product in products]))
+ assert ([False, False, False, False, True] ==
+ sorted([product['installed'] for product in products]))
+ assert (['0', '0', '0', '0', '0'] ==
+ sorted([product['release'] for product in products]))
+ assert ([False, False, False, False, u'sles'] ==
+ sorted([product['productline'] for product in products]))
+ assert ([1509408000, 1522454400, 1522454400, 1730332800, 1730332800] ==
+ sorted([product['eol_t'] for product in products]))
+
+ def test_refresh_db(self):
+ '''
+ Test if refresh DB handled correctly
+ '''
+ ref_out = [
+ "Repository 'openSUSE-Leap-42.1-LATEST' is up to date.",
+ "Repository 'openSUSE-Leap-42.1-Update' is up to date.",
+ "Retrieving repository 'openSUSE-Leap-42.1-Update-Non-Oss' metadata",
+ "Forcing building of repository cache",
+ "Building repository 'openSUSE-Leap-42.1-Update-Non-Oss' cache ..........[done]",
+ "Building repository 'salt-dev' cache",
+ "All repositories have been refreshed."
+ ]
+
+ run_out = {
+ 'stderr': '', 'stdout': '\n'.join(ref_out), 'retcode': 0
+ }
+
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=run_out)}):
+ result = zypper.refresh_db()
+ self.assertEqual(result.get("openSUSE-Leap-42.1-LATEST"), False)
+ self.assertEqual(result.get("openSUSE-Leap-42.1-Update"), False)
+ self.assertEqual(result.get("openSUSE-Leap-42.1-Update-Non-Oss"), True)
+
+ def test_info_installed(self):
+ '''
+ Test the return information of 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': 'noarch', 'size': '17992'},
+
+ '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()
+ # Test overall products length
+ assert len(installed) == 2
+
+ # Test translated fields
+ for pkg_name, pkg_info in installed.items():
+ assert installed[pkg_name].get('source') == run_out[pkg_name]['source_rpm']
+
+ # Test keys transition from the lowpkg.info
+ for pn_key, pn_val in run_out['virgo-dummy'].items():
+ if pn_key == 'source_rpm':
+ continue
+ assert installed['virgo-dummy'][pn_key] == pn_val
+
+ def test_info_available(self):
+ '''
+ Test return the information of the named package available for the system.
+
+ :return:
+ '''
+ test_pkgs = ['vim', 'emacs', 'python']
+ ref_out = get_test_data('zypper-available.txt')
+ with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
+ available = zypper.info_available(*test_pkgs, refresh=False)
+ assert len(available) == 3
+ for pkg_name, pkg_info in available.items():
+ assert pkg_name in test_pkgs
+
+ assert available['emacs']['status'] == 'up-to-date'
+ assert available['emacs']['installed']
+ assert available['emacs']['support level'] == 'Level 3'
+ assert available['emacs']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
+ assert available['emacs']['summary'] == 'GNU Emacs Base Package'
+
+ assert available['vim']['status'] == 'not installed'
+ assert not available['vim']['installed']
+ assert available['vim']['support level'] == 'Level 3'
+ assert available['vim']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
+ assert available['vim']['summary'] == 'Vi IMproved'
+
+ @patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
+ def test_latest_version(self):
+ '''
+ Test the latest version of the named package available for upgrade or installation.
+
+ :return:
+ '''
+ ref_out = get_test_data('zypper-available.txt')
+ with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
+ assert zypper.latest_version('vim') == '7.4.326-2.62'
+
+ @patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
+ def test_upgrade_available(self):
+ '''
+ Test whether or not an upgrade is available for a given package.
+
+ :return:
+ '''
+ ref_out = get_test_data('zypper-available.txt')
+ with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
+ for pkg_name in ['emacs', 'python']:
+ assert not zypper.upgrade_available(pkg_name)
+ assert zypper.upgrade_available('vim')
+
+ @patch('salt.modules.zypper.HAS_RPM', True)
+ def test_version_cmp_rpm(self):
+ '''
+ Test package version is called RPM version if RPM-Python is installed
+
+ :return:
+ '''
+ with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
+ with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
+ assert 0 == zypper.version_cmp('1', '2') # mock returns 0, which means RPM was called
+
+ @patch('salt.modules.zypper.HAS_RPM', False)
+ def test_version_cmp_fallback(self):
+ '''
+ Test package version is called RPM version if RPM-Python is installed
+
+ :return:
+ '''
+ with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
+ with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
+ assert -1 == zypper.version_cmp('1', '2') # mock returns -1, a python implementation was called
+
+ def test_list_pkgs(self):
+ '''
+ Test packages listing.
+
+ :return:
+ '''
+ def _add_data(data, key, value):
+ data[key] = value
+
+ rpm_out = [
+ 'protobuf-java_|-2.6.1_|-3.1.develHead_|-',
+ 'yast2-ftp-server_|-3.1.8_|-8.1_|-',
+ 'jose4j_|-0.4.4_|-2.1.develHead_|-',
+ 'apache-commons-cli_|-1.2_|-1.233_|-',
+ 'jakarta-commons-discovery_|-0.4_|-129.686_|-',
+ 'susemanager-build-keys-web_|-12.0_|-5.1.develHead_|-',
+ ]
+ with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}):
+ with patch.dict(zypper.__salt__, {'pkg_resource.add_pkg': _add_data}):
+ with patch.dict(zypper.__salt__, {'pkg_resource.sort_pkglist': MagicMock()}):
+ with patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}):
+ pkgs = zypper.list_pkgs()
+ for pkg_name, pkg_version in {
+ 'jakarta-commons-discovery': '0.4-129.686',
+ 'yast2-ftp-server': '3.1.8-8.1',
+ 'protobuf-java': '2.6.1-3.1.develHead',
+ 'susemanager-build-keys-web': '12.0-5.1.develHead',
+ 'apache-commons-cli': '1.2-1.233',
+ 'jose4j': '0.4.4-2.1.develHead'}.items():
+ assert pkgs.get(pkg_name)
+ assert pkgs[pkg_name] == pkg_version
+
+ def test_remove_purge(self):
+ '''
+ Test package removal
+ :return:
+ '''
+ class ListPackages(object):
+ def __init__(self):
+ self._packages = ['vim', 'pico']
+ self._pkgs = {
+ 'vim': '0.18.0',
+ 'emacs': '24.0.1',
+ 'pico': '0.1.1',
+ }
+
+ def __call__(self):
+ pkgs = self._pkgs.copy()
+ for target in self._packages:
+ if self._pkgs.get(target):
+ del self._pkgs[target]
+
+ return pkgs
+
+ parsed_targets = [{'vim': None, 'pico': None}, None]
+
+ with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=False)}):
+ with patch.dict(zypper.__salt__, {'pkg_resource.parse_targets': MagicMock(return_value=parsed_targets)}):
+ with patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}):
+ with patch('salt.modules.zypper.list_pkgs', ListPackages()):
+ diff = zypper.remove(name='vim,pico')
+ for pkg_name in ['vim', 'pico']:
+ assert diff.get(pkg_name)
+ assert diff[pkg_name]['old']
+ assert not diff[pkg_name]['new']
+
+
+if __name__ == '__main__':
+ from integration import run_tests
+ run_tests(ZypperTestCase, needs_daemon=False)
--
2.7.2

View File

@ -0,0 +1,296 @@
From 0372b1ff62a79d0c9f384fe48969d8bae039d5a1 Mon Sep 17 00:00:00 2001
From: Michael Calmer <mc@suse.de>
Date: Thu, 25 Feb 2016 10:20:29 +0100
Subject: [PATCH 24/25] proper checking if zypper exit codes and handling of
result messages
add function to check zypper exit codes
check zypper exit code everywhere
add _zypper_check_result() to raise and error or return stdout
use _zypper_check_result()
remove new lines between zypper command and check result
restructure the code a bit
---
salt/modules/zypper.py | 144 +++++++++++++++++++++++++++++--------------------
1 file changed, 85 insertions(+), 59 deletions(-)
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
index ab8bb06..d6628aa 100644
--- a/salt/modules/zypper.py
+++ b/salt/modules/zypper.py
@@ -26,6 +26,7 @@ except ImportError:
# pylint: enable=import-error,redefined-builtin,no-name-in-module
from xml.dom import minidom as dom
+from xml.parsers.expat import ExpatError
# Import salt libs
import salt.utils
@@ -70,6 +71,53 @@ def _zypper(*opts):
return cmd
+def _is_zypper_error(retcode):
+ '''
+ Return True in case the exist code indicate a zypper errror.
+ Otherwise False
+ '''
+ # see man zypper for existing exit codes
+ return not int(retcode) in [0, 100, 101, 102, 103]
+
+
+def _zypper_check_result(result, xml=False):
+ '''
+ Check the result of a zypper command. In case of an error, it raise
+ a CommandExecutionError. Otherwise it returns stdout string of the
+ command.
+
+ result
+ The result of a zypper command called with cmd.run_all
+
+ xml
+ Set to True if zypper command was called with --xmlout.
+ In this case it try to read an error message out of the XML
+ stream. Default is False.
+ '''
+ if _is_zypper_error(result['retcode']):
+ msg = list()
+ if not xml:
+ msg.append(result['stderr'] and result['stderr'] or "")
+ else:
+ try:
+ doc = dom.parseString(result['stdout'])
+ except ExpatError as err:
+ log.error(err)
+ doc = None
+ if doc:
+ msg_nodes = doc.getElementsByTagName('message')
+ for node in msg_nodes:
+ if node.getAttribute('type') == 'error':
+ msg.append(node.childNodes[0].nodeValue)
+ elif result['stderr'].strip():
+ msg.append(result['stderr'].strip())
+
+ raise CommandExecutionError("zypper command failed: {0}".format(
+ msg and os.linesep.join(msg) or "Check zypper logs"))
+
+ return result['stdout']
+
+
def list_upgrades(refresh=True):
'''
List all available package upgrades on this system
@@ -89,15 +137,7 @@ def list_upgrades(refresh=True):
refresh_db()
ret = dict()
run_data = __salt__['cmd.run_all'](_zypper('-x', 'list-updates'), output_loglevel='trace')
- if run_data['retcode'] != 0:
- msg = list()
- for chnl in ['stderr', 'stdout']:
- if run_data.get(chnl, ''):
- msg.append(run_data[chnl])
- raise CommandExecutionError(os.linesep.join(msg) or
- 'Zypper returned non-zero system exit. See Zypper logs for more details.')
-
- doc = dom.parseString(run_data['stdout'])
+ doc = dom.parseString(_zypper_check_result(run_data, xml=True))
for update_node in doc.getElementsByTagName('update'):
if update_node.getAttribute('kind') == 'package':
ret[update_node.getAttribute('name')] = update_node.getAttribute('edition')
@@ -506,7 +546,8 @@ def del_repo(repo):
for alias in repos_cfg.sections():
if alias == repo:
cmd = _zypper('-x', 'rr', '--loose-auth', '--loose-query', alias)
- doc = dom.parseString(__salt__['cmd.run'](cmd, output_loglevel='trace'))
+ ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(ret, xml=True))
msg = doc.getElementsByTagName('message')
if doc.getElementsByTagName('progress') and msg:
return {
@@ -590,22 +631,8 @@ def mod_repo(repo, **kwargs):
'Repository \'{0}\' already exists as \'{1}\'.'.format(repo, alias))
# Add new repo
- doc = None
- try:
- # Try to parse the output and find the error,
- # but this not always working (depends on Zypper version)
- doc = dom.parseString(__salt__['cmd.run'](
- _zypper('-x', 'ar', url, repo), output_loglevel='trace'))
- except Exception:
- # No XML out available, but it is still unknown the state of the result.
- pass
-
- if doc:
- msg_nodes = doc.getElementsByTagName('message')
- if msg_nodes:
- msg_node = msg_nodes[0]
- if msg_node.getAttribute('type') == 'error':
- raise CommandExecutionError(msg_node.childNodes[0].nodeValue)
+ _zypper_check_result(__salt__['cmd.run_all'](_zypper('-x', 'ar', url, repo),
+ output_loglevel='trace'), xml=True)
# Verify the repository has been added
repos_cfg = _get_configured_repos()
@@ -641,8 +668,9 @@ def mod_repo(repo, **kwargs):
if cmd_opt:
cmd_opt.append(repo)
- __salt__['cmd.run'](_zypper('-x', 'mr', *cmd_opt),
- output_loglevel='trace')
+ ret = __salt__['cmd.run_all'](_zypper('-x', 'mr', *cmd_opt),
+ output_loglevel='trace')
+ _zypper_check_result(ret, xml=True)
# If repo nor added neither modified, error should be thrown
if not added and not cmd_opt:
@@ -666,17 +694,7 @@ def refresh_db():
'''
cmd = _zypper('refresh', '--force')
ret = {}
- call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
- if call['retcode'] != 0:
- comment = ''
- if 'stderr' in call:
- comment += call['stderr']
-
- raise CommandExecutionError(
- '{0}'.format(comment)
- )
- else:
- out = call['stdout']
+ out = _zypper_check_result(__salt__['cmd.run_all'](cmd, output_loglevel='trace'))
for line in out.splitlines():
if not line:
@@ -828,19 +846,18 @@ def install(name=None,
cmd = cmd_install + targets[:500]
targets = targets[500:]
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False)
- if call['retcode'] != 0:
- raise CommandExecutionError(call['stderr']) # Fixme: This needs a proper report mechanism.
- else:
- for line in call['stdout'].splitlines():
- match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
- if match:
- downgrades.append(match.group(1))
+ out = _zypper_check_result(call)
+ for line in out.splitlines():
+ match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
+ if match:
+ downgrades.append(match.group(1))
while downgrades:
cmd = cmd_install + ['--force'] + downgrades[:500]
downgrades = downgrades[500:]
- __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False)
+ _zypper_check_result(__salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False))
+
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
@@ -877,7 +894,7 @@ def upgrade(refresh=True):
old = list_pkgs()
cmd = _zypper('update', '--auto-agree-with-licenses')
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
- if call['retcode'] != 0:
+ if _is_zypper_error(call['retcode']):
ret['result'] = False
if 'stderr' in call:
ret['comment'] += call['stderr']
@@ -906,7 +923,8 @@ def _uninstall(name=None, pkgs=None):
return {}
while targets:
- __salt__['cmd.run'](_zypper('remove', *targets[:500]), output_loglevel='trace')
+ _zypper_check_result(__salt__['cmd.run_all'](_zypper('remove', *targets[:500]),
+ output_loglevel='trace'))
targets = targets[500:]
__context__.pop('pkg.list_pkgs', None)
@@ -1019,7 +1037,8 @@ def clean_locks():
if not os.path.exists("/etc/zypp/locks"):
return out
- doc = dom.parseString(__salt__['cmd.run'](_zypper('-x', 'cl'), output_loglevel='trace'))
+ ret = __salt__['cmd.run_all'](_zypper('-x', 'cl'), output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(ret, xml=True))
for node in doc.getElementsByTagName("message"):
text = node.childNodes[0].nodeValue.lower()
if text.startswith(LCK):
@@ -1057,7 +1076,8 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument
missing.append(pkg)
if removed:
- __salt__['cmd.run'](_zypper('rl', *removed), output_loglevel='trace')
+ _zypper_check_result(__salt__['cmd.run_all'](_zypper('rl', *removed),
+ output_loglevel='trace'))
return {'removed': len(removed), 'not_found': missing}
@@ -1086,7 +1106,8 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument
added.append(pkg)
if added:
- __salt__['cmd.run'](_zypper('al', *added), output_loglevel='trace')
+ _zypper_check_result(__salt__['cmd.run_all'](_zypper('al', *added),
+ output_loglevel='trace'))
return {'added': len(added), 'packages': added}
@@ -1218,8 +1239,10 @@ def _get_patterns(installed_only=None):
List all known patterns in repos.
'''
patterns = {}
- doc = dom.parseString(__salt__['cmd.run'](_zypper('--xmlout', 'se', '-t', 'pattern'),
- output_loglevel='trace'))
+
+ ret = __salt__['cmd.run_all'](_zypper('--xmlout', 'se', '-t', 'pattern'),
+ output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(ret, xml=True))
for element in doc.getElementsByTagName('solvable'):
installed = element.getAttribute('status') == 'installed'
if (installed_only and installed) or not installed_only:
@@ -1283,8 +1306,9 @@ def search(criteria, refresh=False):
if refresh:
refresh_db()
- doc = dom.parseString(__salt__['cmd.run'](_zypper('--xmlout', 'se', criteria),
- output_loglevel='trace'))
+ ret = __salt__['cmd.run_all'](_zypper('--xmlout', 'se', criteria),
+ output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(ret, xml=True))
solvables = doc.getElementsByTagName('solvable')
if not solvables:
raise CommandExecutionError('No packages found by criteria "{0}".'.format(criteria))
@@ -1343,7 +1367,9 @@ def list_products(all=False, refresh=False):
cmd = _zypper('-x', 'products')
if not all:
cmd.append('-i')
- doc = dom.parseString(__salt__['cmd.run'](cmd, output_loglevel='trace'))
+
+ call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(call, xml=True))
for prd in doc.getElementsByTagName('product-list')[0].getElementsByTagName('product'):
p_nfo = dict()
for k_p_nfo, v_p_nfo in prd.attributes.items():
@@ -1390,8 +1416,8 @@ def download(*packages, **kwargs):
if refresh:
refresh_db()
- doc = dom.parseString(__salt__['cmd.run'](
- _zypper('-x', 'download', *packages), output_loglevel='trace'))
+ ret = __salt__['cmd.run_all'](_zypper('-x', 'download', *packages), output_loglevel='trace')
+ doc = dom.parseString(_zypper_check_result(ret, xml=True))
pkg_ret = {}
for dld_result in doc.getElementsByTagName("download-result"):
repo = dld_result.getElementsByTagName("repository")[0]
--
2.1.4

View File

@ -0,0 +1,288 @@
From de4417cd3de8af72fe2acd2ea22ab7c04327a939 Mon Sep 17 00:00:00 2001
From: Michael Calmer <mc@suse.de>
Date: Fri, 26 Feb 2016 12:05:45 +0100
Subject: [PATCH 25/25] adapt tests to new zypper_check_result() output
test _zypper_check_result()
use specialized assert functions for tests
---
tests/unit/modules/zypper_test.py | 158 ++++++++++++++++++++++++++------------
1 file changed, 111 insertions(+), 47 deletions(-)
diff --git a/tests/unit/modules/zypper_test.py b/tests/unit/modules/zypper_test.py
index de964f9..f89d18f 100644
--- a/tests/unit/modules/zypper_test.py
+++ b/tests/unit/modules/zypper_test.py
@@ -57,12 +57,63 @@ class ZypperTestCase(TestCase):
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
upgrades = zypper.list_upgrades(refresh=False)
- assert len(upgrades) == 3
+ self.assertEqual(len(upgrades), 3)
for pkg, version in {'SUSEConnect': '0.2.33-7.1',
'bind-utils': '9.9.6P1-35.1',
'bind-libs': '9.9.6P1-35.1'}.items():
- assert pkg in upgrades
- assert upgrades[pkg] == version
+ self.assertIn(pkg, upgrades)
+ self.assertEqual(upgrades[pkg], version)
+
+ def test_zypper_check_result(self):
+ '''
+ Test zypper check result function
+ '''
+ cmd_out = {
+ 'retcode': 1,
+ 'stdout': '',
+ 'stderr': 'This is an error'
+ }
+ with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: This is an error$"):
+ zypper._zypper_check_result(cmd_out)
+
+ cmd_out = {
+ 'retcode': 0,
+ 'stdout': 'result',
+ 'stderr': ''
+ }
+ out = zypper._zypper_check_result(cmd_out)
+ self.assertEqual(out, "result")
+
+ cmd_out = {
+ 'retcode': 1,
+ 'stdout': '',
+ 'stderr': 'This is an error'
+ }
+ with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: This is an error$"):
+ zypper._zypper_check_result(cmd_out, xml=True)
+
+ cmd_out = {
+ 'retcode': 1,
+ 'stdout': '',
+ 'stderr': ''
+ }
+ with self.assertRaisesRegexp(CommandExecutionError, "^zypper command failed: Check zypper logs$"):
+ zypper._zypper_check_result(cmd_out, xml=True)
+
+ cmd_out = {
+ 'stdout': '''<?xml version='1.0'?>
+<stream>
+ <message type="info">Refreshing service &apos;container-suseconnect&apos;.</message>
+ <message type="error">Some handled zypper internal error</message>
+ <message type="error">Another zypper internal error</message>
+</stream>
+ ''',
+ 'stderr': '',
+ 'retcode': 1
+ }
+ with self.assertRaisesRegexp(CommandExecutionError,
+ "^zypper command failed: Some handled zypper internal error\nAnother zypper internal error$"):
+ zypper._zypper_check_result(cmd_out, xml=True)
def test_list_upgrades_error_handling(self):
'''
@@ -71,45 +122,53 @@ class ZypperTestCase(TestCase):
'''
# Test handled errors
ref_out = {
- 'stderr': 'Some handled zypper internal error',
+ 'stdout': '''<?xml version='1.0'?>
+<stream>
+ <message type="info">Refreshing service &apos;container-suseconnect&apos;.</message>
+ <message type="error">Some handled zypper internal error</message>
+ <message type="error">Another zypper internal error</message>
+</stream>
+ ''',
'retcode': 1
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
- try:
+ with self.assertRaisesRegexp(CommandExecutionError,
+ "^zypper command failed: Some handled zypper internal error\nAnother zypper internal error$"):
zypper.list_upgrades(refresh=False)
- except CommandExecutionError as error:
- assert error.message == ref_out['stderr']
# Test unhandled error
ref_out = {
- 'retcode': 1
+ 'retcode': 1,
+ 'stdout': '',
+ 'stderr': ''
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
- try:
+ with self.assertRaisesRegexp(CommandExecutionError, '^zypper command failed: Check zypper logs$'):
zypper.list_upgrades(refresh=False)
- except CommandExecutionError as error:
- assert error.message == 'Zypper returned non-zero system exit. See Zypper logs for more details.'
def test_list_products(self):
'''
List products test.
'''
- ref_out = get_test_data('zypper-products.xml')
- with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=ref_out)}):
+ ref_out = {
+ 'retcode': 0,
+ 'stdout': get_test_data('zypper-products.xml')
+ }
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
products = zypper.list_products()
- assert len(products) == 5
- assert (['SLES', 'SLES', 'SUSE-Manager-Proxy', 'SUSE-Manager-Server', 'sle-manager-tools-beta'] ==
+ self.assertEqual(len(products), 5)
+ self.assertEqual(['SLES', 'SLES', 'SUSE-Manager-Proxy', 'SUSE-Manager-Server', 'sle-manager-tools-beta'],
sorted([prod['name'] for prod in products]))
- assert ('SUSE LLC <https://www.suse.com/>' in [product['vendor'] for product in products])
- assert ([False, False, False, False, True] ==
+ self.assertIn('SUSE LLC <https://www.suse.com/>', [product['vendor'] for product in products])
+ self.assertEqual([False, False, False, False, True],
sorted([product['isbase'] for product in products]))
- assert ([False, False, False, False, True] ==
+ self.assertEqual([False, False, False, False, True],
sorted([product['installed'] for product in products]))
- assert (['0', '0', '0', '0', '0'] ==
+ self.assertEqual(['0', '0', '0', '0', '0'],
sorted([product['release'] for product in products]))
- assert ([False, False, False, False, u'sles'] ==
+ self.assertEqual([False, False, False, False, u'sles'],
sorted([product['productline'] for product in products]))
- assert ([1509408000, 1522454400, 1522454400, 1730332800, 1730332800] ==
+ self.assertEqual([1509408000, 1522454400, 1522454400, 1730332800, 1730332800],
sorted([product['eol_t'] for product in products]))
def test_refresh_db(self):
@@ -168,17 +227,17 @@ class ZypperTestCase(TestCase):
with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}):
installed = zypper.info_installed()
# Test overall products length
- assert len(installed) == 2
+ self.assertEqual(len(installed), 2)
# Test translated fields
for pkg_name, pkg_info in installed.items():
- assert installed[pkg_name].get('source') == run_out[pkg_name]['source_rpm']
+ self.assertEqual(installed[pkg_name].get('source'), run_out[pkg_name]['source_rpm'])
# Test keys transition from the lowpkg.info
for pn_key, pn_val in run_out['virgo-dummy'].items():
if pn_key == 'source_rpm':
continue
- assert installed['virgo-dummy'][pn_key] == pn_val
+ self.assertEqual(installed['virgo-dummy'][pn_key], pn_val)
def test_info_available(self):
'''
@@ -190,21 +249,21 @@ class ZypperTestCase(TestCase):
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
available = zypper.info_available(*test_pkgs, refresh=False)
- assert len(available) == 3
+ self.assertEqual(len(available), 3)
for pkg_name, pkg_info in available.items():
- assert pkg_name in test_pkgs
+ self.assertIn(pkg_name, test_pkgs)
- assert available['emacs']['status'] == 'up-to-date'
- assert available['emacs']['installed']
- assert available['emacs']['support level'] == 'Level 3'
- assert available['emacs']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
- assert available['emacs']['summary'] == 'GNU Emacs Base Package'
+ self.assertEqual(available['emacs']['status'], 'up-to-date')
+ self.assertTrue(available['emacs']['installed'])
+ self.assertEqual(available['emacs']['support level'], 'Level 3')
+ self.assertEqual(available['emacs']['vendor'], 'SUSE LLC <https://www.suse.com/>')
+ self.assertEqual(available['emacs']['summary'], 'GNU Emacs Base Package')
- assert available['vim']['status'] == 'not installed'
- assert not available['vim']['installed']
- assert available['vim']['support level'] == 'Level 3'
- assert available['vim']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
- assert available['vim']['summary'] == 'Vi IMproved'
+ self.assertEqual(available['vim']['status'], 'not installed')
+ self.assertFalse(available['vim']['installed'])
+ self.assertEqual(available['vim']['support level'], 'Level 3')
+ self.assertEqual(available['vim']['vendor'], 'SUSE LLC <https://www.suse.com/>')
+ self.assertEqual(available['vim']['summary'], 'Vi IMproved')
@patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
def test_latest_version(self):
@@ -215,7 +274,7 @@ class ZypperTestCase(TestCase):
'''
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
- assert zypper.latest_version('vim') == '7.4.326-2.62'
+ self.assertEqual(zypper.latest_version('vim'), '7.4.326-2.62')
@patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
def test_upgrade_available(self):
@@ -227,8 +286,8 @@ class ZypperTestCase(TestCase):
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
for pkg_name in ['emacs', 'python']:
- assert not zypper.upgrade_available(pkg_name)
- assert zypper.upgrade_available('vim')
+ self.assertFalse(zypper.upgrade_available(pkg_name))
+ self.assertTrue(zypper.upgrade_available('vim'))
@patch('salt.modules.zypper.HAS_RPM', True)
def test_version_cmp_rpm(self):
@@ -239,7 +298,7 @@ class ZypperTestCase(TestCase):
'''
with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
- assert 0 == zypper.version_cmp('1', '2') # mock returns 0, which means RPM was called
+ self.assertEqual(0, zypper.version_cmp('1', '2')) # mock returns 0, which means RPM was called
@patch('salt.modules.zypper.HAS_RPM', False)
def test_version_cmp_fallback(self):
@@ -250,7 +309,7 @@ class ZypperTestCase(TestCase):
'''
with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
- assert -1 == zypper.version_cmp('1', '2') # mock returns -1, a python implementation was called
+ self.assertEqual(-1, zypper.version_cmp('1', '2')) # mock returns -1, a python implementation was called
def test_list_pkgs(self):
'''
@@ -281,8 +340,8 @@ class ZypperTestCase(TestCase):
'susemanager-build-keys-web': '12.0-5.1.develHead',
'apache-commons-cli': '1.2-1.233',
'jose4j': '0.4.4-2.1.develHead'}.items():
- assert pkgs.get(pkg_name)
- assert pkgs[pkg_name] == pkg_version
+ self.assertTrue(pkgs.get(pkg_name))
+ self.assertEqual(pkgs[pkg_name], pkg_version)
def test_remove_purge(self):
'''
@@ -307,16 +366,21 @@ class ZypperTestCase(TestCase):
return pkgs
parsed_targets = [{'vim': None, 'pico': None}, None]
+ cmd_out = {
+ 'retcode': 0,
+ 'stdout': '',
+ 'stderr': ''
+ }
- with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=False)}):
+ with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=cmd_out)}):
with patch.dict(zypper.__salt__, {'pkg_resource.parse_targets': MagicMock(return_value=parsed_targets)}):
with patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}):
with patch('salt.modules.zypper.list_pkgs', ListPackages()):
diff = zypper.remove(name='vim,pico')
for pkg_name in ['vim', 'pico']:
- assert diff.get(pkg_name)
- assert diff[pkg_name]['old']
- assert not diff[pkg_name]['new']
+ self.assertTrue(diff.get(pkg_name))
+ self.assertTrue(diff[pkg_name]['old'])
+ self.assertFalse(diff[pkg_name]['new'])
if __name__ == '__main__':
--
2.1.4

View File

@ -0,0 +1,27 @@
From 80784a70e90d16c5d8290fcc6bf8a0f4ec657ec0 Mon Sep 17 00:00:00 2001
From: Michael Calmer <mc@suse.de>
Date: Fri, 4 Mar 2016 09:51:22 +0100
Subject: [PATCH 26/26] do not generate a date in a comment to prevent rebuilds
(bsc#969407)
---
setup.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/setup.py b/setup.py
index 8caa45e..dd76c64 100755
--- a/setup.py
+++ b/setup.py
@@ -600,8 +600,7 @@ class Clean(clean):
INSTALL_VERSION_TEMPLATE = '''\
-# This file was auto-generated by salt's setup on \
-{date:%A, %d %B %Y @ %H:%m:%S UTC}.
+# This file was auto-generated by salt's setup
from salt.version import SaltStackVersion
--
2.1.4

View File

@ -0,0 +1,21 @@
From 9b8d6cbb72cd6537016a9d4da73bd3127b951845 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcus=20R=C3=BCckert?= <mrueckert@suse.de>
Date: Wed, 2 Mar 2016 17:29:23 +0100
Subject: [PATCH] make the suse check consistent with rh_service.py
---
salt/modules/service.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/salt/modules/service.py b/salt/modules/service.py
index 05db855..7aacedd 100644
--- a/salt/modules/service.py
+++ b/salt/modules/service.py
@@ -37,6 +37,7 @@ def __virtual__():
'Arch ARM',
'ALT',
'SUSE Enterprise Server',
+ 'SUSE',
'OEL',
'Linaro',
'elementary OS',

View File

@ -0,0 +1,28 @@
From c0c8a77242cac5565febc9b08aeda7328d13e92f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcus=20R=C3=BCckert?= <mrueckert@suse.de>
Date: Wed, 2 Mar 2016 17:29:54 +0100
Subject: [PATCH] Fix numerical check of osrelease
After making the version check numerical in 9975508 it no longer matched
SLES 11 properly to use the rh_service module as '11.4 > 11' evaluates
to true. Without using the rh_service module, not all methods are
implemented to use the service state on sle11.
---
salt/modules/rh_service.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/salt/modules/rh_service.py b/salt/modules/rh_service.py
index c425cde..910a75d 100644
--- a/salt/modules/rh_service.py
+++ b/salt/modules/rh_service.py
@@ -66,8 +66,8 @@ def __virtual__():
return (False, 'Cannot load rh_service module: '
'osrelease grain, {0}, not a float,'.format(osrelease))
if __grains__['os'] == 'SUSE':
- if osrelease > 11:
- return (False, 'Cannot load rh_service module on SUSE >= 11')
+ if osrelease >= 12:
+ return (False, 'Cannot load rh_service module on SUSE >= 12')
if __grains__['os'] == 'Fedora':
if osrelease > 15:
return (False, 'Cannot load rh_service module on Fedora >= 15')

View File

@ -1,3 +1,37 @@
-------------------------------------------------------------------
Fri Mar 4 10:41:52 UTC 2016 - tampakrap@opensuse.org
- Fix the service state / module on SLE11.
Add:
* 0027-make-suse-check-consistent-with-rh_service.patch
* 0028-fix-numerical-check-of-osrelease.patch
-------------------------------------------------------------------
Fri Mar 4 09:54:00 CET 2016 - mc@suse.de
- Prevent rebuilds in OBS by not generating a date as a comment in
a source file
Add: 0026-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch
-------------------------------------------------------------------
Fri Feb 26 14:55:14 CET 2016 - mc@suse.de
- Add better checking for zypper exit codes and simplify evaluation
of the zypper error messages.
Add: 0024-proper-checking-if-zypper-exit-codes-and-handling-of.patch
- Adapt unit tests
Add: 0025-adapt-tests-to-new-zypper_check_result-output.patch
-------------------------------------------------------------------
Fri Feb 26 10:42:17 UTC 2016 - bmaryniuk@suse.com
- Add initial pack of Zypper's Unit tests.
Use XML output in list_upgrades.
Bugfix: upgrade_available crashes when only one package specified
Purge is not using "-u" anymore
Add:
* 0023-Initial-Zypper-Unit-Tests-and-bugfixes.patch
-------------------------------------------------------------------
Tue Feb 23 11:58:00 CET 2016 - mc@suse.de
@ -80,6 +114,9 @@ Wed Feb 10 08:30:45 UTC 2016 - dmacvicar@suse.de
Mon Feb 8 16:15:56 UTC 2016 - kkaempf@suse.com
- Update to 2015.8.5
Security fixes:
* CVE-2016-1866: Improper handling of clear messages on the
minion remote code execution (boo#965403)
See https://docs.saltstack.com/en/latest/topics/releases/2015.8.5.html
Dropped patches (all upstream):
* 0003-List-products-consistently-across-all-SLES-systems.patch

View File

@ -85,6 +85,17 @@ Patch20: 0020-simplify-checking-the-refresh-paramater.patch
Patch21: 0021-do-not-change-kwargs-in-refresh-while-checking-a-val.patch
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/31429
Patch22: 0022-fix-argument-handling-for-pkg.download.patch
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/31479
# https://github.com/saltstack/salt/pull/31488
Patch23: 0023-Initial-Zypper-Unit-Tests-and-bugfixes.patch
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/31508
Patch24: 0024-proper-checking-if-zypper-exit-codes-and-handling-of.patch
Patch25: 0025-adapt-tests-to-new-zypper_check_result-output.patch
# PATCH-FIX-OPENSUSE prevent rebuilds in OBS
Patch26: 0026-do-not-generate-a-date-in-a-comment-to-prevent-rebui.patch
# PATCH-FIX-UPSTREAM https://github.com/saltstack/salt/pull/31629
Patch27: 0027-make-suse-check-consistent-with-rh_service.patch
Patch28: 0028-fix-numerical-check-of-osrelease.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: logrotate
@ -449,6 +460,12 @@ cp %{S:1} .
%patch20 -p1
%patch21 -p1
%patch22 -p1
%patch23 -p1
%patch24 -p1
%patch25 -p1
%patch26 -p1
%patch27 -p1
%patch28 -p1
%build
python setup.py --salt-transport=both build