"""
YumLog - file ``/var/log/yum.log``
==================================
This module provides parsing for the ``/var/log/yum.log`` file.
"""
from collections import defaultdict, namedtuple
from insights.core import Parser
from insights.core.exceptions import ParseException, SkipComponent
from insights.core.filters import add_filter
from insights.core.plugins import parser
from insights.parsers.installed_rpms import InstalledRpm
from insights.specs import Specs
Entry = namedtuple('Entry', field_names='idx timestamp state pkg')
"""namedtuple: Represents a package line in ``/var/log/yum.log``."""
VALID_STATES = ["Erased:", "Installed:", "Updated:"]
"""Package states in this file"""
add_filter(Specs.yum_log, VALID_STATES)
[docs]
@parser(Specs.yum_log)
class YumLog(Parser, list):
"""
Class for parsing file ``/var/log/yum.log`` into a list of .
The file looks like::
May 13 15:54:49 Installed: libevent-2.0.21-4.el7.x86_64
May 13 15:54:49 Installed: tmux-1.8-4.el7.x86_64
May 23 18:06:24 Installed: wget-1.14-10.el7_0.1.x86_64
May 23 18:10:05 Updated: 1:openssl-libs-1.0.1e-51.el7_2.5.x86_64
May 23 18:10:05 Installed: 1:perl-parent-0.225-244.el7.noarch
May 23 18:10:05 Installed: perl-HTTP-Tiny-0.033-3.el7.noarch
May 23 16:09:09 Erased: redhat-access-insights-batch
May 23 18:10:05 Installed: perl-podlators-2.5.1-3.el7.noarch
May 23 18:10:05 Installed: perl-Pod-Perldoc-3.20-4.el7.noarch
May 23 18:10:05 Installed: 1:perl-Pod-Escapes-1.04-286.el7.noarch
May 23 18:10:06 Installed: perl-Text-ParseWords-3.29-4.el7.noarch
The information is stored as a ``list`` of :py:data:`Entry` objects, each
of which contains attributes for the position in the log, timestamp of the
action, the package's state in the system, and the affected package as an
:py:class:`insights.parsers.installed_rpms.InstalledRpm`.
.. note::
The file will be filtered at least with the keywords defined in the
:py:data:`STATES`, that means only the lines that contain one of
these keywords will be collected.
Examples:
>>> type(yl)
<class 'insights.parsers.yumlog.YumLog'>
>>> e = yl.present_packages.get('nss-softokn')
>>> e.pkg.release == '19.el6_6'
True
>>> e = yl.present_packages.get('openssl-libs')
>>> e.pkg.name == 'openssl-libs'
True
>>> e.pkg.version == '1.0.1e'
True
>>> len(yl)
8
>>> sorted(e.idx for e in yl) == sorted(range(len(yl)))
True
"""
ERASED = 'Erased'
"""
.. deprecated:: 3.2.0
Will be removed from 3.4.0. Use string "Erased" instead.
"""
INSTALLED = 'Installed'
"""
.. deprecated:: 3.2.0
Will be removed from 3.4.0. Use string "Installed" instead.
"""
UPDATED = 'Updated'
"""
.. deprecated:: 3.2.0
Will be removed from 3.4.0. Use string "Updated" instead.
"""
STATES = set(["Erased", "Installed", "Updated"])
[docs]
def parse_content(self, content):
"""Parses contents of each line in ``/var/log/yum.log``.
Each line in the file contains 5 fields that are parsed into the
attributes of :py:data:`Entry` instances:
- month
- day
- time
- state
- package
The month, day, and time form the ``Entry.timestamp``.
``Entry.state`` contains the state of the package, one of ``Erased``,
``Installed``, or ``Updated``. ``Entry.pkg`` contains the
:py:class:`insights.parsers.installed_rpms.InstalledRpm` instance
corresponding to the parse package.
``Entry.idx`` is the zero-based line number (but not the RAW line number)
of the ``Entry`` in the file. It can ONLY be used to tell ordering
of events.
Parameters:
content (list): Lines of ``/var/log/yum.log`` to be parsed.
Raises:
ParseException: if a line can't be parsed for any reason.
SkipComponent: if nothing is parsed.
"""
self._pkgs = defaultdict(list)
for idx, line in enumerate(content):
if not any(s in line for s in self.STATES): # Do not remove
continue
try:
line = line.replace(': 100', '')
month, day, time, state, pkg = line.split()[:5]
timestamp = ' '.join([month, day, time])
state = state.rstrip(':')
pkg = pkg.split(':')[-1].strip()
if "." not in pkg:
pkg = InstalledRpm({'name': pkg})
else:
pkg = InstalledRpm.from_package(pkg)
e = Entry(idx, timestamp, state, pkg)
self.append(e)
self._pkgs[pkg.name].append(e)
except:
raise ParseException('YumLog could not parse', line)
if len(self) == 0:
raise SkipComponent
self.__cur_pkgs = dict((k, v[-1]) for k, v in self._pkgs.items())
@property
def pkgs(self):
"""
.. deprecated:: 3.2.0
Will be removed from 3.4.0.
"""
return dict(self._pkgs)
@property
def data(self):
"""
.. deprecated:: 3.2.0
Will be removed from 3.4.0.
"""
return self
@property
def present_packages(self):
"""`list` of latest ``Entry`` instances for installed packages."""
return dict((k, v) for k, v in self.__cur_pkgs.items() if v.state in ['Installed', 'Updated'])
[docs]
def packages_of(self, state):
"""
Parameters:
state: one state or list of states of `Installed`, `Updated`, and `Erased`.
Returns:
`list` of latest ``Entry`` instances for packages in `state`.
Raises:
KeyError: when getting invalid state.
"""
state = [state] if isinstance(state, str) else state
if any(st not in self.STATES for st in state):
raise KeyError('Invalid State(s): {0}'.format(state))
return dict((k, v) for k, v in self.__cur_pkgs.items() if v.state in state)