summaryrefslogtreecommitdiffstats
path: root/utils/crm_pkg.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/crm_pkg.py')
-rwxr-xr-xutils/crm_pkg.py342
1 files changed, 342 insertions, 0 deletions
diff --git a/utils/crm_pkg.py b/utils/crm_pkg.py
new file mode 100755
index 0000000..b775004
--- /dev/null
+++ b/utils/crm_pkg.py
@@ -0,0 +1,342 @@
+#!/usr/bin/python3
+# Copyright (C) 2013 Kristoffer Gronlund <kgronlund@suse.com>
+# See COPYING for license information.
+
+import os
+import sys
+import subprocess
+import json
+
+
+DRY_RUN = False
+
+
+def get_platform():
+ return os.uname()[0]
+
+
+def fail(msg):
+ print(msg, file=sys.stderr)
+ sys.exit(1)
+
+
+def run(cmd):
+ proc = subprocess.Popen(cmd,
+ shell=False,
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = proc.communicate(None)
+ return proc.returncode, out, err
+
+
+def is_program(prog):
+ """Is this program available?"""
+ for p in os.getenv("PATH").split(os.pathsep):
+ filename = os.path.join(p, prog)
+ if os.path.isfile(filename) and os.access(filename, os.X_OK):
+ return filename
+ return None
+
+
+class PackageManager(object):
+ def dispatch(self, name, state):
+ if state in ('installed', 'present'):
+ return self.present(name)
+ elif state in ('absent', 'removed'):
+ return self.absent(name)
+ elif state == 'latest':
+ return self.latest(name)
+ fail(msg="Unknown state: " + state)
+
+ def present(self, name):
+ raise NotImplementedError
+
+ def latest(self, name):
+ raise NotImplementedError
+
+ def absent(self, name):
+ raise NotImplementedError
+
+
+class Zypper(PackageManager):
+ def __init__(self):
+ self._rpm = is_program('rpm')
+ self._zyp = is_program('zypper')
+ if self._rpm is None or self._zyp is None:
+ raise OSError("Missing tools: %s, %s" % (self._rpm, self._zyp))
+
+ def get_version(self, name):
+ cmd = [self._rpm, '-q', name]
+ rc, stdout, stderr = run(cmd)
+ if rc == 0:
+ for line in stdout.splitlines():
+ if name in line.decode('utf-8'):
+ return line.strip()
+ return None
+
+ def is_installed(self, name):
+ if not isinstance(self._rpm, str):
+ raise IOError(str(self._rpm))
+ if not isinstance(name, str):
+ raise IOError(str(name))
+ cmd = [self._rpm, '--query', '--info', name]
+ rc, stdout, stderr = run(cmd)
+ return rc == 0
+
+ def present(self, name):
+ if self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._zyp,
+ '--non-interactive',
+ '--no-refresh',
+ 'install',
+ '--auto-agree-with-licenses',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+ def latest(self, name):
+ if not self.is_installed(name):
+ return self.present(name)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ pre_version = self.get_version(name)
+ cmd = [self._zyp,
+ '--non-interactive',
+ '--no-refresh',
+ 'update',
+ '--auto-agree-with-licenses',
+ name]
+ rc, stdout, stderr = run(cmd)
+ post_version = self.get_version(name)
+ changed = pre_version != post_version
+ return (rc, stdout, stderr, changed)
+
+ def absent(self, name):
+ if not self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._zyp,
+ '--non-interactive',
+ 'remove',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+
+class Yum(PackageManager):
+ def __init__(self):
+ self._rpm = is_program('rpm')
+ self._yum = is_program('yum')
+
+ def get_version(self, name):
+ cmd = [self._rpm, '-q', name]
+ rc, stdout, stderr = run(cmd)
+ if rc == 0:
+ for line in stdout.splitlines():
+ if name in line:
+ return line.strip()
+ return None
+
+ def is_installed(self, name):
+ cmd = [self._rpm, '--query', '--info', name]
+ rc, stdout, stderr = run(cmd)
+ return rc == 0
+
+ def present(self, name):
+ if self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._yum,
+ '--assumeyes',
+ '-d', '2',
+ 'install',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+ def latest(self, name):
+ if not self.is_installed(name):
+ return self.present(name)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ pre_version = self.get_version(name)
+ cmd = [self._yum,
+ '--assumeyes',
+ '-d', '2',
+ 'update',
+ name]
+ rc, stdout, stderr = run(cmd)
+ post_version = self.get_version(name)
+ changed = pre_version != post_version
+ return (rc, stdout, stderr, changed)
+
+ def absent(self, name):
+ if not self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._yum,
+ '--assumeyes',
+ '-d', '2',
+ 'erase',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+
+class Apt(PackageManager):
+ def __init__(self):
+ self._apt = is_program('apt-get')
+ self._dpkg = is_program('dpkg')
+ if self._apt is None or self._dpkg is None:
+ raise OSError("Missing tools: %s, %s" % (self._apt, self._dpkg))
+
+ def get_version(self, name):
+ cmd = [self._dpkg, '--status', name]
+ rc, stdout, stderr = run(cmd)
+ if rc == 0:
+ for line in stdout.splitlines():
+ info = line.decode('utf-8').split(':', 1)
+ if len(info) == 2 and info[0] == 'Version':
+ return info[1].strip()
+ return None
+
+ def is_installed(self, name):
+ cmd = [self._dpkg, '--status', name]
+ rc, stdout, stderr = run(cmd)
+ if rc == 0:
+ for line in stdout.splitlines():
+ info = line.decode('utf-8').split(':', 1)
+ if len(info) == 2 and info[0] == 'Status':
+ return info[1].strip().endswith('installed')
+ return False
+
+ def present(self, name):
+ if self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._apt,
+ '--assume-yes',
+ '--quiet',
+ 'install',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+ def latest(self, name):
+ if not self.is_installed(name):
+ return self.present(name)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ pre_version = self.get_version(name)
+ cmd = [self._apt,
+ '--assume-yes',
+ '--quiet',
+ '--only-upgrade',
+ 'install',
+ name]
+ rc, stdout, stderr = run(cmd)
+ post_version = self.get_version(name)
+ changed = pre_version != post_version
+ return (rc, stdout, stderr, changed)
+
+ def absent(self, name):
+ if not self.is_installed(name):
+ return (0, b'', b'', False)
+
+ if DRY_RUN:
+ return (0, b'', b'', True)
+
+ cmd = [self._apt,
+ '--assume-yes',
+ '--quiet',
+ 'purge',
+ name]
+ rc, stdout, stderr = run(cmd)
+ changed = rc == 0
+ return (rc, stdout, stderr, changed)
+
+
+class Pacman(PackageManager):
+ pass
+
+
+def manage_package(pkg, state):
+ """
+ Gathers version and release information about a package.
+ """
+ if pkg is None:
+ raise IOError("PKG IS NONE")
+ pf = get_platform()
+ if pf != 'Linux':
+ fail(msg="Unsupported platform: " + pf)
+ managers = {
+ 'zypper': Zypper,
+ 'yum': Yum,
+ 'apt-get': Apt,
+ #'pacman': Pacman
+ }
+ for name, mgr in managers.items():
+ exe = is_program(name)
+ if exe:
+ rc, stdout, stderr, changed = mgr().dispatch(pkg, state)
+ return {'rc': rc,
+ 'stdout': stdout.decode('utf-8'),
+ 'stderr': stderr.decode('utf-8'),
+ 'changed': changed
+ }
+ fail(msg="No supported package manager found")
+
+
+def main():
+ import argparse
+ parser = argparse.ArgumentParser(
+ description="(Semi)-Universal package installer",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument('-d', '--dry-run', dest='dry_run', action='store_true',
+ help="Only check if changes would be made")
+
+ parser.add_argument('-n', '--name', metavar='name', type=str,
+ help="Name of package")
+
+ parser.add_argument('-s', '--state', metavar='state', type=str,
+ help="Desired state (present|latest|removed)", default="present")
+
+ args = parser.parse_args()
+ global DRY_RUN
+ DRY_RUN = args.dry_run
+ if not args.name or not args.state:
+ raise IOError("Bad arguments: %s" % (sys.argv))
+ data = manage_package(args.name, args.state)
+ print(json.dumps(str(data)))
+
+main()