yum also parse obsolete package output (#45365)

* yum also parse obsolete package output

This is a rebase of the patch originally proposed in
https://github.com/ansible/ansible/pull/40001 by machacekondra

Fixes #39978

Signed-off-by: Adam Miller <admiller@redhat.com>

* properly parse the obsoletes, provide a new output entry, add changelog

Signed-off-by: Adam Miller <admiller@redhat.com>

* make pep8 happy

Signed-off-by: Adam Miller <admiller@redhat.com>

* remove q debugging output

Signed-off-by: Adam Miller <admiller@redhat.com>
This commit is contained in:
Adam Miller 2018-11-06 15:07:50 -06:00 committed by ansibot
parent 6c94c28a12
commit 091fb1dc3f
3 changed files with 51 additions and 19 deletions

View file

@ -0,0 +1,3 @@
---
minor_changes:
- "yum - when checking for updates, now properly include Obsoletes (both old and new) package data in the module JSON output, fixes https://github.com/ansible/ansible/issues/39978"

View file

@ -1075,6 +1075,7 @@ class YumModule(YumDnf):
@staticmethod @staticmethod
def parse_check_update(check_update_output): def parse_check_update(check_update_output):
updates = {} updates = {}
obsoletes = {}
# remove incorrect new lines in longer columns in output from yum check-update # remove incorrect new lines in longer columns in output from yum check-update
# yum line wrapping can move the repo to the next line # yum line wrapping can move the repo to the next line
@ -1099,17 +1100,24 @@ class YumModule(YumDnf):
# ignore irrelevant lines # ignore irrelevant lines
# '*' in line matches lines like mirror lists: # '*' in line matches lines like mirror lists:
# * base: mirror.corbina.net # * base: mirror.corbina.net
# len(line) != 3 could be junk or a continuation # len(line) != 3 or 6 could be junk or a continuation
# len(line) = 6 is package obsoletes
# #
# FIXME: what is the '.' not in line conditional for? # FIXME: what is the '.' not in line conditional for?
if '*' in line or len(line) != 3 or '.' not in line[0]: if '*' in line or len(line) not in [3, 6] or '.' not in line[0]:
continue continue
else: else:
pkg, version, repo = line pkg, version, repo = line[0], line[1], line[2]
name, dist = pkg.rsplit('.', 1) name, dist = pkg.rsplit('.', 1)
updates.update({name: {'version': version, 'dist': dist, 'repo': repo}}) updates.update({name: {'version': version, 'dist': dist, 'repo': repo}})
return updates
if len(line) == 6:
obsolete_pkg, obsolete_version, obsolete_repo = line[3], line[4], line[5]
obsolete_name, obsolete_dist = obsolete_pkg.rsplit('.', 1)
obsoletes.update({obsolete_name: {'version': obsolete_version, 'dist': obsolete_dist, 'repo': obsolete_repo}})
return updates, obsoletes
def latest(self, items, repoq): def latest(self, items, repoq):
@ -1122,6 +1130,7 @@ class YumModule(YumDnf):
pkgs['update'] = [] pkgs['update'] = []
pkgs['install'] = [] pkgs['install'] = []
updates = {} updates = {}
obsoletes = {}
update_all = False update_all = False
cmd = None cmd = None
@ -1135,7 +1144,7 @@ class YumModule(YumDnf):
res['results'].append('Nothing to do here, all packages are up to date') res['results'].append('Nothing to do here, all packages are up to date')
return res return res
elif rc == 100: elif rc == 100:
updates = self.parse_check_update(out) updates, obsoletes = self.parse_check_update(out)
elif rc == 1: elif rc == 1:
res['msg'] = err res['msg'] = err
res['rc'] = rc res['rc'] = rc
@ -1267,6 +1276,9 @@ class YumModule(YumDnf):
if will_update or pkgs['install']: if will_update or pkgs['install']:
res['changed'] = True res['changed'] = True
if obsoletes:
res['obsoletes'] = obsoletes
return res return res
# run commands # run commands
@ -1291,6 +1303,9 @@ class YumModule(YumDnf):
if rc: if rc:
res['failed'] = True res['failed'] = True
if obsoletes:
res['obsoletes'] = obsoletes
return res return res
def ensure(self, repoq): def ensure(self, repoq):

View file

@ -127,9 +127,19 @@ glibc.x86_64 2.17-157.el7_3.1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
unwrapped_output_rhel7_obsoletes = unwrapped_output_rhel7 + wrapped_output_rhel7_obsoletes_postfix unwrapped_output_rhel7_obsoletes = unwrapped_output_rhel7 + wrapped_output_rhel7_obsoletes_postfix
unwrapped_output_rhel7_expected_pkgs = ["NetworkManager-openvpn", "NetworkManager-openvpn-gnome", "cabal-install", unwrapped_output_rhel7_expected_new_obsoletes_pkgs = [
"ddashboard", "python-bugzilla", "python2-futures", "python2-pip",
"python2-pyxdg", "python2-simplejson"
]
unwrapped_output_rhel7_expected_old_obsoletes_pkgs = [
"developerdashboard", "python-bugzilla-develdashboardfixes",
"python-futures", "python-pip", "pyxdg", "python-simplejson"
]
unwrapped_output_rhel7_expected_updated_pkgs = [
"NetworkManager-openvpn", "NetworkManager-openvpn-gnome", "cabal-install",
"cgit", "python34-libs", "python34-test", "python34-tkinter", "cgit", "python34-libs", "python34-test", "python34-tkinter",
"python34-tools", "qgit", "rdiff-backup", "stoken-libs", "xlockmore"] "python34-tools", "qgit", "rdiff-backup", "stoken-libs", "xlockmore"
]
class TestYumUpdateCheckParse(unittest.TestCase): class TestYumUpdateCheckParse(unittest.TestCase):
@ -141,34 +151,34 @@ class TestYumUpdateCheckParse(unittest.TestCase):
self.assertIsInstance(result, dict) self.assertIsInstance(result, dict)
def test_empty_output(self): def test_empty_output(self):
res = YumModule.parse_check_update("") res, obs = YumModule.parse_check_update("")
expected_pkgs = [] expected_pkgs = []
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_longname(self): def test_longname(self):
res = YumModule.parse_check_update(longname) res, obs = YumModule.parse_check_update(longname)
expected_pkgs = ['xxxxxxxxxxxxxxxxxxxxxxxxxx', 'glibc'] expected_pkgs = ['xxxxxxxxxxxxxxxxxxxxxxxxxx', 'glibc']
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_plugin_load_error(self): def test_plugin_load_error(self):
res = YumModule.parse_check_update(yum_plugin_load_error) res, obs = YumModule.parse_check_update(yum_plugin_load_error)
expected_pkgs = [] expected_pkgs = []
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_wrapped_output_1(self): def test_wrapped_output_1(self):
res = YumModule.parse_check_update(wrapped_output_1) res, obs = YumModule.parse_check_update(wrapped_output_1)
expected_pkgs = ["vms-agent"] expected_pkgs = ["vms-agent"]
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_wrapped_output_2(self): def test_wrapped_output_2(self):
res = YumModule.parse_check_update(wrapped_output_2) res, obs = YumModule.parse_check_update(wrapped_output_2)
expected_pkgs = ["empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty", expected_pkgs = ["empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty",
"libtiff"] "libtiff"]
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_wrapped_output_3(self): def test_wrapped_output_3(self):
res = YumModule.parse_check_update(wrapped_output_3) res, obs = YumModule.parse_check_update(wrapped_output_3)
expected_pkgs = ["ceph", "ceph-base", "ceph-common", "ceph-mds", expected_pkgs = ["ceph", "ceph-base", "ceph-common", "ceph-mds",
"ceph-mon", "ceph-osd", "ceph-selinux", "libcephfs1", "ceph-mon", "ceph-osd", "ceph-selinux", "libcephfs1",
"librados2", "libradosstriper1", "librbd1", "librgw2", "librados2", "libradosstriper1", "librbd1", "librgw2",
@ -176,16 +186,20 @@ class TestYumUpdateCheckParse(unittest.TestCase):
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_wrapped_output_4(self): def test_wrapped_output_4(self):
res = YumModule.parse_check_update(wrapped_output_4) res, obs = YumModule.parse_check_update(wrapped_output_4)
expected_pkgs = ["ipxe-roms-qemu", "quota", "quota-nls", "rdma", "screen", expected_pkgs = ["ipxe-roms-qemu", "quota", "quota-nls", "rdma", "screen",
"sos", "sssd-client"] "sos", "sssd-client"]
self._assert_expected(expected_pkgs, res) self._assert_expected(expected_pkgs, res)
def test_wrapped_output_rhel7(self): def test_wrapped_output_rhel7(self):
res = YumModule.parse_check_update(unwrapped_output_rhel7) res, obs = YumModule.parse_check_update(unwrapped_output_rhel7)
self._assert_expected(unwrapped_output_rhel7_expected_pkgs, res) self._assert_expected(unwrapped_output_rhel7_expected_updated_pkgs, res)
def test_wrapped_output_rhel7_obsoletes(self): def test_wrapped_output_rhel7_obsoletes(self):
res = YumModule.parse_check_update(unwrapped_output_rhel7_obsoletes) res, obs = YumModule.parse_check_update(unwrapped_output_rhel7_obsoletes)
self._assert_expected(unwrapped_output_rhel7_expected_pkgs, res) self._assert_expected(
unwrapped_output_rhel7_expected_updated_pkgs + unwrapped_output_rhel7_expected_new_obsoletes_pkgs,
res
)
self._assert_expected(unwrapped_output_rhel7_expected_old_obsoletes_pkgs, obs)