blob: b77d7bf8f5d1c42220dc19463ab8c19796da8b33 [file] [edit]
#!/usr/bin/python
# 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 >>sys.stderr, msg
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:
return line.strip()
return None
def is_installed(self, name):
if not isinstance(self._rpm, basestring):
raise IOError(str(self._rpm))
if not isinstance(name, basestring):
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, '', '', False)
if DRY_RUN:
return (0, '', '', 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, '', '', 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, '', '', False)
if DRY_RUN:
return (0, '', '', 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, '', '', False)
if DRY_RUN:
return (0, '', '', 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, '', '', 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, '', '', False)
if DRY_RUN:
return (0, '', '', 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):
pass
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.iteritems():
exe = is_program(name)
if exe:
rc, stdout, stderr, changed = mgr().dispatch(pkg, state)
return {'rc': rc,
'stdout': stdout,
'stderr': stderr,
'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(data)
main()