From 2dbe1b96d6ef60f8010c7f5c443c753aa89fdf9b Mon Sep 17 00:00:00 2001
From: Erik Johnson <palehose@gmail.com>
Date: Thu, 4 Jan 2018 16:10:18 -0600
Subject: [PATCH] yumpkg: don't use diff_attr when determining
 install/downgrade targets

Doing so breaks epoch handling, and is unnecessary anyway since the
diff_attr is only used for the return data.

Additionally, this tweaks the "attr" argument in both yumpkg and
zypper's list_pkgs func so that it will accept a comma-separated list as
well as a Python list, and makes a DRY tweak so that we're only
returning and formatting the return data in one place in the function.
---
 salt/modules/yumpkg.py | 85 ++++++++++++++++++++++++------------------
 salt/modules/zypper.py | 45 ++++++++++++----------
 2 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py
index 20a6484c26..f5110d650d 100644
--- a/salt/modules/yumpkg.py
+++ b/salt/modules/yumpkg.py
@@ -622,6 +622,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
     .. code-block:: bash
 
         salt '*' pkg.list_pkgs
+        salt '*' pkg.list_pkgs attr=version,arch
         salt '*' pkg.list_pkgs attr='["version", "arch"]'
     '''
     versions_as_list = salt.utils.is_true(versions_as_list)
@@ -630,42 +631,52 @@ def list_pkgs(versions_as_list=False, **kwargs):
             for x in ('removed', 'purge_desired')]):
         return {}
 
-    attr = kwargs.get("attr")
-    if 'pkg.list_pkgs' in __context__:
-        cached = __context__['pkg.list_pkgs']
-        return __salt__['pkg_resource.format_pkg_list'](cached, versions_as_list, attr)
+    attr = kwargs.get('attr')
+    if attr is not None:
+        attr = salt.utils.split_input(attr)
 
-    ret = {}
-    cmd = ['rpm', '-qa', '--queryformat',
-           salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n']
-    output = __salt__['cmd.run'](cmd,
-                                 python_shell=False,
-                                 output_loglevel='trace')
-    for line in output.splitlines():
-        pkginfo = salt.utils.pkg.rpm.parse_pkginfo(
-            line,
-            osarch=__grains__['osarch']
-        )
-        if pkginfo is not None:
-            # see rpm version string rules available at https://goo.gl/UGKPNd
-            pkgver = pkginfo.version
-            epoch = ''
-            release = ''
-            if ':' in pkgver:
-                epoch, pkgver = pkgver.split(":", 1)
-            if '-' in pkgver:
-                pkgver, release = pkgver.split("-", 1)
-            all_attr = {'epoch': epoch, 'version': pkgver, 'release': release,
-                        'arch': pkginfo.arch, 'install_date': pkginfo.install_date,
-                        'install_date_time_t': pkginfo.install_date_time_t}
-            __salt__['pkg_resource.add_pkg'](ret, pkginfo.name, all_attr)
-
-    for pkgname in ret:
-        ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version'])
-
-    __context__['pkg.list_pkgs'] = ret
+    contextkey = 'pkg.list_pkgs'
 
-    return __salt__['pkg_resource.format_pkg_list'](ret, versions_as_list, attr)
+    if contextkey not in __context__:
+        ret = {}
+        cmd = ['rpm', '-qa', '--queryformat',
+               salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n']
+        output = __salt__['cmd.run'](cmd,
+                                     python_shell=False,
+                                     output_loglevel='trace')
+        for line in output.splitlines():
+            pkginfo = salt.utils.pkg.rpm.parse_pkginfo(
+                line,
+                osarch=__grains__['osarch']
+            )
+            if pkginfo is not None:
+                # see rpm version string rules available at https://goo.gl/UGKPNd
+                pkgver = pkginfo.version
+                epoch = ''
+                release = ''
+                if ':' in pkgver:
+                    epoch, pkgver = pkgver.split(":", 1)
+                if '-' in pkgver:
+                    pkgver, release = pkgver.split("-", 1)
+                all_attr = {
+                    'epoch': epoch,
+                    'version': pkgver,
+                    'release': release,
+                    'arch': pkginfo.arch,
+                    'install_date': pkginfo.install_date,
+                    'install_date_time_t': pkginfo.install_date_time_t
+                }
+                __salt__['pkg_resource.add_pkg'](ret, pkginfo.name, all_attr)
+
+        for pkgname in ret:
+            ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version'])
+
+        __context__[contextkey] = ret
+
+    return __salt__['pkg_resource.format_pkg_list'](
+        __context__[contextkey],
+        versions_as_list,
+        attr)
 
 
 def list_repo_pkgs(*args, **kwargs):
@@ -1195,7 +1206,6 @@ def install(name=None,
     if pkg_params is None or len(pkg_params) == 0:
         return {}
 
-    old = list_pkgs(versions_as_list=False) if not downloadonly else list_downloaded()
     version_num = kwargs.get('version')
     if version_num:
         if pkgs is None and sources is None:
@@ -1205,10 +1215,11 @@ def install(name=None,
             log.warning('"version" parameter will be ignored for multiple '
                         'package targets')
 
-    diff_attr = kwargs.get("diff_attr")
+    diff_attr = kwargs.get('diff_attr')
+    old = list_pkgs(versions_as_list=False, attr=diff_attr) if not downloadonly else list_downloaded()
     # Use of __context__ means no duplicate work here, just accessing
     # information already in __context__ from the previous call to list_pkgs()
-    old_as_list = list_pkgs(versions_as_list=True, attr=diff_attr) if not downloadonly else list_downloaded()
+    old_as_list = list_pkgs(versions_as_list=True) if not downloadonly else list_downloaded()
 
     to_install = []
     to_downgrade = []
diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py
index be95322309..eab509fd5f 100644
--- a/salt/modules/zypper.py
+++ b/salt/modules/zypper.py
@@ -620,6 +620,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
     .. code-block:: bash
 
         salt '*' pkg.list_pkgs
+        salt '*' pkg.list_pkgs attr=version,arch
         salt '*' pkg.list_pkgs attr='["version", "arch"]'
     '''
     versions_as_list = salt.utils.is_true(versions_as_list)
@@ -628,30 +629,36 @@ def list_pkgs(versions_as_list=False, **kwargs):
             for x in ('removed', 'purge_desired')]):
         return {}
 
-    attr = kwargs.get("attr")
-    if 'pkg.list_pkgs' in __context__:
-        cached = __context__['pkg.list_pkgs']
-        return __salt__['pkg_resource.format_pkg_list'](cached, versions_as_list, attr)
+    attr = kwargs.get('attr')
+    if attr is not None:
+        attr = salt.utils.split_input(attr)
 
-    cmd = ['rpm', '-qa', '--queryformat', (
-        "%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-"
-        "%|EPOCH?{%{EPOCH}}:{}|_|-%{INSTALLTIME}\\n")]
-    ret = {}
-    for line in __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False).splitlines():
-        name, pkgver, rel, arch, epoch, install_time = line.split('_|-')
-        install_date = datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z"
-        install_date_time_t = int(install_time)
+    contextkey = 'pkg.list_pkgs'
+
+    if contextkey not in __context__:
+
+        cmd = ['rpm', '-qa', '--queryformat', (
+            "%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-"
+            "%|EPOCH?{%{EPOCH}}:{}|_|-%{INSTALLTIME}\\n")]
+        ret = {}
+        for line in __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False).splitlines():
+            name, pkgver, rel, arch, epoch, install_time = line.split('_|-')
+            install_date = datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z"
+            install_date_time_t = int(install_time)
 
-        all_attr = {'epoch': epoch, 'version': pkgver, 'release': rel, 'arch': arch,
-                    'install_date': install_date, 'install_date_time_t': install_date_time_t}
-        __salt__['pkg_resource.add_pkg'](ret, name, all_attr)
+            all_attr = {'epoch': epoch, 'version': pkgver, 'release': rel, 'arch': arch,
+                        'install_date': install_date, 'install_date_time_t': install_date_time_t}
+            __salt__['pkg_resource.add_pkg'](ret, name, all_attr)
 
-    for pkgname in ret:
-        ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version'])
+        for pkgname in ret:
+            ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version'])
 
-    __context__['pkg.list_pkgs'] = ret
+        __context__[contextkey] = ret
 
-    return __salt__['pkg_resource.format_pkg_list'](ret, versions_as_list, attr)
+    return __salt__['pkg_resource.format_pkg_list'](
+        __context__[contextkey],
+        versions_as_list,
+        attr)
 
 
 def _get_configured_repos():
-- 
2.17.1


