diff options
Diffstat (limited to 'whatmaps')
-rwxr-xr-x | whatmaps/command.py | 42 | ||||
-rw-r--r-- | whatmaps/debiandistro.py | 59 | ||||
-rw-r--r-- | whatmaps/debianpkg.py | 6 | ||||
-rw-r--r-- | whatmaps/distro.py | 32 | ||||
-rw-r--r-- | whatmaps/pkg.py | 5 | ||||
-rw-r--r-- | whatmaps/process.py | 1 | ||||
-rw-r--r-- | whatmaps/redhatdistro.py | 8 | ||||
-rw-r--r-- | whatmaps/rpmpkg.py | 5 | ||||
-rw-r--r-- | whatmaps/systemd.py | 3 |
9 files changed, 106 insertions, 55 deletions
diff --git a/whatmaps/command.py b/whatmaps/command.py index 0f8e433..e28a8b9 100755 --- a/whatmaps/command.py +++ b/whatmaps/command.py @@ -17,7 +17,6 @@ # - import errno import glob import os @@ -30,15 +29,16 @@ from . distro import Distro from . pkg import PkgError from . systemd import Systemd + def check_maps(procs, shared_objects): restart_procs = {} for proc in procs: for so in shared_objects: if proc.maps(so): if proc.exe in restart_procs: - restart_procs[proc.exe] += [ proc ] + restart_procs[proc.exe] += [proc] else: - restart_procs[proc.exe] = [ proc ] + restart_procs[proc.exe] = [proc] break return restart_procs @@ -78,7 +78,7 @@ def find_pkgs(procs, distro): if pkg.name in pkgs: pkgs[pkg.name].procs.append(proc) else: - pkg.procs = [ proc ] + pkg.procs = [proc] pkgs[pkg.name] = pkg if pkgs: @@ -117,21 +117,32 @@ def find_systemd_units(procmap, distro): try: unit = Systemd.process_to_unit(proc) except ValueError as e: - logging.warning("No systemd unit found for '%s': %s" + logging.warning("No systemd unit found for '%s': %s " "- restart manually" % (proc.exe, e)) continue if not unit: - logging.warning("No systemd unit found for '%s'" + logging.warning("No systemd unit found for '%s' " "- restart manually" % proc.exe) else: units.add(unit) - units -= set([ service + '.service' for service in distro.service_blacklist ]) + units -= set([service + '.service' for service in distro.service_blacklist]) return units +def filter_services(distro, services): + filtered = distro.filter_services(services) + diff = services - filtered + if len(diff): + logging.warning("Filtered out blacklisted service%s %s - restart manually", + 's' if len(diff) > 1 else '', + ', '.join(diff)) + return filtered + + def main(argv): shared_objects = [] services = None + ret = 0 parser = OptionParser(usage='%prog [options] pkg1 [pkg2 pkg3 pkg4]') parser.add_option("--debug", action="store_true", dest="debug", @@ -157,7 +168,7 @@ def main(argv): logging.basicConfig(level=level, format='%(levelname)s: %(message)s') - distro = Distro.detect() + distro = Distro.detect()() if not distro: logging.error("Unsupported Distribution") return 1 @@ -165,7 +176,7 @@ def main(argv): logging.debug("Detected distribution: '%s'", distro.id) if args: - pkgs = [ distro.pkg(arg) for arg in args ] + pkgs = [distro.pkg(arg) for arg in args] elif options.apt and distro.has_apt(): try: pkgs = distro.read_apt_pipeline() @@ -174,7 +185,9 @@ def main(argv): return 1 if not pkgs: return 0 - pkgs = distro.filter_security_updates(pkgs) + pkgs, notfound = distro.filter_security_updates(pkgs) + if notfound: + logging.warning("Pkgs %s not found in apt cache" % ", ".join(notfound)) logging.debug("Security Upgrades: %s" % pkgs) else: parser.print_help() @@ -185,8 +198,8 @@ def main(argv): try: shared_objects += pkg.shared_objects except PkgError: - logging.error("Cannot parse contents of %s" % pkg.name) - return 1 + logging.error("Cannot parse contents of %s - skipping it" % pkg.name) + ret = 1 logging.debug("Found shared objects:") for so in shared_objects: logging.debug(" %s", so) @@ -226,6 +239,8 @@ def main(argv): else: return 0 + services = filter_services(distro, services) + if options.restart: if options.print_cmds and services: write_cmd_file(services, options.print_cmds, distro) @@ -238,7 +253,8 @@ def main(argv): for s in services: print(s) - return 0 + return ret + def run(): return(main(sys.argv)) diff --git a/whatmaps/debiandistro.py b/whatmaps/debiandistro.py index 32f3774..4fcce54 100644 --- a/whatmaps/debiandistro.py +++ b/whatmaps/debiandistro.py @@ -36,30 +36,37 @@ from . debianpkg import DebianPkg from . pkg import PkgError from . systemd import Systemd + class DebianDistro(Distro): "Debian (dpkg) based distribution" id = 'Debian' - _pkg_services = { 'apache2-mpm-worker': [ 'apache2' ], - 'apache2-mpm-prefork': [ 'apache2' ], - 'apache2.2-bin': [ 'apache2' ], - 'apache2-bin': [ 'apache2' ], - 'dovecot-imapd': [ 'dovecot' ], - 'dovecot-pop3d': [ 'dovecot' ], - 'exim4-daemon-light': [ 'exim4' ], - 'exim4-daemon-heavy': [ 'exim4' ], - 'libvirt-daemon': [ 'libvirtd' ], - 'openjdk-6-jre-headless': ['jenkins', 'tomcat7'], - 'openjdk-7-jre-headless': ['jenkins', 'tomcat7'], - 'qemu-system-x86_64': [ 'libvirt-guests' ], - } + _pkg_services = { + 'apache2-mpm-worker': ['apache2'], + 'apache2-mpm-prefork': ['apache2'], + 'apache2.2-bin': ['apache2'], + 'apache2-bin': ['apache2'], + 'dovecot-imapd': ['dovecot'], + 'dovecot-pop3d': ['dovecot'], + 'exim4-daemon-light': ['exim4'], + 'exim4-daemon-heavy': ['exim4'], + 'libvirt-daemon': ['libvirtd'], + 'openjdk-6-jre-headless': ['jenkins', 'tomcat7'], + 'openjdk-7-jre-headless': ['jenkins', 'tomcat7'], + 'qemu-system-x86_64': ['libvirt-guests'], + } # Per package blacklist - _pkg_service_blacklist = { 'libvirt-bin': [ 'libvirt-guests' ] } + _pkg_service_blacklist = {'libvirt-bin': ['libvirt-guests']} # Per distro blacklist service_blacklist = set(['kvm', 'qemu-kvm', 'qemu-system-x86']) + # Per distro regex filter + service_blacklist_re = set([ + '^user@[0-9]+.service$', # Restarting systemd user service aborts the session + ]) + @classmethod def pkg(klass, name): return DebianPkg(name) @@ -67,8 +74,8 @@ class DebianDistro(Distro): @classmethod def pkg_by_file(klass, path): find_file = subprocess.Popen(['dpkg-query', '-S', path], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) output = find_file.communicate()[0] if find_file.returncode: return None @@ -124,12 +131,11 @@ class DebianDistro(Distro): (pkgname, oldversion, compare, newversion, filename) = line.split() if filename == '**CONFIGURE**': - if oldversion != '-': # Updates only + if oldversion != '-': # Updates only pkgs[pkgname] = DebianPkg(pkgname) pkgs[pkgname].version = newversion return pkgs - @classmethod def _security_update_origins(klass): "Determine security update origins from apt configuration" @@ -138,9 +144,10 @@ class DebianDistro(Distro): raise PkgError("lsb_release not found, can't determine security updates") codename = lsb_release.get_distro_information()['CODENAME'] + def _subst(line): - mapping = {'distro_codename' : codename, - 'distro_id' : klass.id, } + mapping = {'distro_codename': codename, + 'distro_id': klass.id, } return string.Template(line).substitute(mapping) origins = [] @@ -151,7 +158,6 @@ class DebianDistro(Distro): logging.debug("Security Update Origins: %s", origins) return origins - @classmethod def filter_security_updates(klass, pkgs): """Filter on security updates""" @@ -160,14 +166,19 @@ class DebianDistro(Distro): raise PkgError("apt_pkg not installed, can't determine security updates") apt_pkg.init() - acquire = apt_pkg.Acquire() + apt_pkg.Acquire() cache = apt_pkg.Cache() security_update_origins = klass._security_update_origins() security_updates = {} + notfound = [] for pkg in list(pkgs.values()): - cache_pkg = cache[pkg.name] + try: + cache_pkg = cache[pkg.name] + except KeyError: + notfound.append(pkg) + continue for cache_version in cache_pkg.version_list: if pkg.version == cache_version.ver_str: for pfile, _ in cache_version.file_list: @@ -176,4 +187,4 @@ class DebianDistro(Distro): pfile.archive == origin[1]: security_updates[pkg] = pkg break - return security_updates + return (security_updates, notfound) diff --git a/whatmaps/debianpkg.py b/whatmaps/debianpkg.py index 8a3349d..0192513 100644 --- a/whatmaps/debianpkg.py +++ b/whatmaps/debianpkg.py @@ -19,17 +19,18 @@ import re from . pkg import Pkg + class DebianPkg(Pkg): type = 'Debian' _init_script_re = re.compile('/etc/init.d/[\w\-\.]') - _list_contents = ['dpkg-query', '-L', '${pkg_name}' ] + _list_contents = ['dpkg-query', '-L', '${pkg_name}'] def __init__(self, name): Pkg.__init__(self, name) @property def services(self): - if self._services != None: + if self._services is not None: return self._services self._services = [] @@ -39,4 +40,3 @@ class DebianPkg(Pkg): if self._init_script_re.match(line): self._services.append(os.path.basename(line.strip())) return self._services - diff --git a/whatmaps/distro.py b/whatmaps/distro.py index f0f08eb..29d2dc2 100644 --- a/whatmaps/distro.py +++ b/whatmaps/distro.py @@ -17,6 +17,7 @@ import logging import os +import re import subprocess @@ -25,6 +26,7 @@ try: except ImportError: lsb_release = None + class Distro(object): """ A distribution @@ -32,6 +34,8 @@ class Distro(object): @cvar id: distro id as returned by lsb-release @cvar service_blacklist: services that should never be restarted + @cvar service_blacklist_re: regex list of services that should + never be restartet @cvar _pkg_services: A C{dict} that maps packages to services. In case we find binaries that match a key in this hash restart the services listed in values. @@ -40,6 +44,8 @@ class Distro(object): """ id = None service_blacklist = set() + service_blacklist_re = set() + _pkg_services = {} _pkg_blacklist = {} _pkg_service_blacklist = {} @@ -75,8 +81,8 @@ class Distro(object): List of services that package pkg needs restarted that aren't part of pkg itself """ - return [ s for s in klass._pkg_services.get(pkg.name, []) - if klass.is_service_installed(s) ] + return [s for s in klass._pkg_services.get(pkg.name, []) + if klass.is_service_installed(s)] @classmethod def pkg_service_blacklist(klass, pkg): @@ -86,6 +92,17 @@ class Distro(object): """ return klass._pkg_service_blacklist.get(pkg.name, []) + def filter_services(self, services): + """ + Filter out servies that match service_blacklist_re + """ + ret = [] + matchers = [re.compile(b) for b in self.service_blacklist_re] + for s in services: + if not any([m.match(s) for m in matchers]): + ret.append(s) + return set(ret) + @classmethod def has_apt(klass): """Does the distribution use apt""" @@ -95,8 +112,9 @@ class Distro(object): def detect(): return detect() -import whatmaps.debiandistro -import whatmaps.redhatdistro +import whatmaps.debiandistro # noqa: E402 +import whatmaps.redhatdistro # noqa: E402 + def detect(): """ @@ -110,11 +128,11 @@ def detect(): else: try: lsb_cmd = subprocess.Popen(['lsb_release', '--id', '-s'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) output = lsb_cmd.communicate()[0] if not lsb_cmd.returncode: - id = output.strip() + id = output.strip() except OSError: # id is None in this case pass diff --git a/whatmaps/pkg.py b/whatmaps/pkg.py index 4290d0b..7aafd5a 100644 --- a/whatmaps/pkg.py +++ b/whatmaps/pkg.py @@ -19,6 +19,7 @@ import re import string import subprocess + class PkgError(Exception): pass @@ -55,8 +56,8 @@ class Pkg(object): if self._contents: return self._contents else: - cmd = [ string.Template(arg).substitute(arg, pkg_name=self.name) - for arg in self._list_contents ] + cmd = [string.Template(arg).substitute(arg, pkg_name=self.name) + for arg in self._list_contents] list_contents = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/whatmaps/process.py b/whatmaps/process.py index a39da89..78165a7 100644 --- a/whatmaps/process.py +++ b/whatmaps/process.py @@ -20,6 +20,7 @@ import logging import os import re + class Process(object): """A process - Linux only so far, needs /proc mounted""" deleted_re = re.compile(r"(?P<exe>.*) \(deleted\)$") diff --git a/whatmaps/redhatdistro.py b/whatmaps/redhatdistro.py index 0427c30..4b17f3c 100644 --- a/whatmaps/redhatdistro.py +++ b/whatmaps/redhatdistro.py @@ -21,10 +21,11 @@ import subprocess from . distro import Distro from . rpmpkg import RpmPkg + class RedHatDistro(Distro): "RPM based distribution""" _pkg_re = re.compile(r'(?P<pkg>[\w\-\+]+)-(?P<ver>[\w\.]+)' - '-(?P<rel>[\w\.]+)\.(?P<arch>.+)') + '-(?P<rel>[\w\.]+)\.(?P<arch>.+)') @classmethod def pkg(klass, name): @@ -33,8 +34,8 @@ class RedHatDistro(Distro): @classmethod def pkg_by_file(klass, path): find_file = subprocess.Popen(['rpm', '-qf', path], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) output = find_file.communicate()[0] if find_file.returncode: return None @@ -52,4 +53,3 @@ class RedHatDistro(Distro): class FedoraDistro(RedHatDistro): id = 'Fedora' - diff --git a/whatmaps/rpmpkg.py b/whatmaps/rpmpkg.py index 595edd2..493dd5a 100644 --- a/whatmaps/rpmpkg.py +++ b/whatmaps/rpmpkg.py @@ -19,17 +19,18 @@ import re from . pkg import Pkg + class RpmPkg(Pkg): type = 'RPM' _init_script_re = re.compile('/etc/rc.d/init.d/[\w\-\.]') - _list_contents = [ 'rpm', '-ql', '$pkg_name' ] + _list_contents = ['rpm', '-ql', '$pkg_name'] def __init__(self, name): Pkg.__init__(self, name) @property def services(self): - if self._services != None: + if self._services is not None: return self._services self._services = [] diff --git a/whatmaps/systemd.py b/whatmaps/systemd.py index 9bc03b1..d4f45fe 100644 --- a/whatmaps/systemd.py +++ b/whatmaps/systemd.py @@ -43,5 +43,8 @@ class Systemd(object): return parts[0] elif parts[1].endswith('.service'): return parts[1] + elif parts[1].startswith('session-') and parts[1].endswith('.scope'): + msg = output.decode('utf-8').split('\n')[0][2:] + raise ValueError("Can't parse service name from %s" % msg) else: raise ValueError("Can't parse service name from: (%s %s)" % (parts[0], parts[1])) |