diff options
Diffstat (limited to 'src/sbin/bcfg2-yum-helper')
-rwxr-xr-x | src/sbin/bcfg2-yum-helper | 161 |
1 files changed, 138 insertions, 23 deletions
diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper index 7dbdad16b..227d977de 100755 --- a/src/sbin/bcfg2-yum-helper +++ b/src/sbin/bcfg2-yum-helper @@ -10,10 +10,14 @@ import sys import yum import logging import Bcfg2.Logger +from Bcfg2.Compat import wraps +from lockfile import FileLock, LockTimeout from optparse import OptionParser try: import json -except ImportError: + # py2.4 json library is structured differently + json.loads # pylint: disable=W0104 +except (ImportError, AttributeError): import simplejson as json @@ -42,8 +46,8 @@ def pkgtup_to_string(package): return ''.join(str(e) for e in rv) -class DepSolver(object): - """ Yum dependency solver """ +class YumHelper(object): + """ Yum helper base object """ def __init__(self, cfgfile, verbose=1): self.cfgfile = cfgfile @@ -57,6 +61,16 @@ class DepSolver(object): self.yumbase._getConfig(cfgfile, debuglevel=verbose) # pylint: enable=E1121,W0212 self.logger = logging.getLogger(self.__class__.__name__) + + +class DepSolver(YumHelper): + """ Yum dependency solver. This is used for operations that only + read from the yum cache, and thus operates in cacheonly mode. """ + + def __init__(self, cfgfile, verbose=1): + YumHelper.__init__(self, cfgfile, verbose=verbose) + # internally, yum uses an integer, not a boolean, for conf.cache + self.yumbase.conf.cache = 1 self._groups = None def get_groups(self): @@ -181,6 +195,45 @@ class DepSolver(object): packages.add(txmbr.pkgtup) return list(packages), list(unknown) + +def acquire_lock(func): + """ decorator for CacheManager methods that gets and release a + lock while the method runs """ + @wraps(func) + def inner(self, *args, **kwargs): + """ Get and release a lock while running the function this + wraps. """ + self.logger.debug("Acquiring lock at %s" % self.lockfile) + while not self.lock.i_am_locking(): + try: + self.lock.acquire(timeout=60) # wait up to 60 seconds + except LockTimeout: + self.lock.break_lock() + self.lock.acquire() + try: + func(self, *args, **kwargs) + finally: + self.lock.release() + self.logger.debug("Released lock at %s" % self.lockfile) + + return inner + + +class CacheManager(YumHelper): + """ Yum cache manager. Unlike :class:`DepSolver`, this can write + to the yum cache, and so is used for operations that muck with the + cache. (Technically, :func:`CacheManager.clean_cache` could be in + either DepSolver or CacheManager, but for consistency I've put it + here.) """ + + def __init__(self, cfgfile, verbose=1): + YumHelper.__init__(self, cfgfile, verbose=verbose) + self.lockfile = \ + os.path.join(os.path.dirname(self.yumbase.conf.config_file_path), + "lock") + self.lock = FileLock(self.lockfile) + + @acquire_lock def clean_cache(self): """ clean the yum cache """ for mdtype in ["Headers", "Packages", "Sqlite", "Metadata", @@ -193,6 +246,27 @@ class DepSolver(object): if not msg.startswith("0 "): self.logger.info(msg) + @acquire_lock + def populate_cache(self): + """ populate the yum cache """ + for repo in self.yumbase.repos.findRepos('*'): + repo.metadata_expire = 0 + repo.mdpolicy = "group:all" + self.yumbase.doRepoSetup() + self.yumbase.repos.doSetup() + for repo in self.yumbase.repos.listEnabled(): + # this populates the cache as a side effect + repo.repoXML # pylint: disable=W0104 + try: + repo.getGroups() + except yum.Errors.RepoMDError: + pass # this repo has no groups + self.yumbase.repos.populateSack(mdtype='metadata', cacheonly=1) + self.yumbase.repos.populateSack(mdtype='filelists', cacheonly=1) + self.yumbase.repos.populateSack(mdtype='otherdata', cacheonly=1) + # this does something with the groups cache as a side effect + self.yumbase.comps # pylint: disable=W0104 + def main(): parser = OptionParser() @@ -221,29 +295,70 @@ def main(): logger.error("Config file %s not found" % options.config) return 1 - depsolver = DepSolver(options.config, options.verbose) + # pylint: disable=W0702 + rv = 0 if cmd == "clean": - depsolver.clean_cache() - print(json.dumps(True)) + cachemgr = CacheManager(options.config, options.verbose) + try: + cachemgr.clean_cache() + print(json.dumps(True)) + except: + logger.error("Unexpected error cleaning cache: %s" % + sys.exc_info()[1], exc_info=1) + print(json.dumps(False)) + rv = 2 + elif cmd == "makecache": + cachemgr = CacheManager(options.config, options.verbose) + try: + # this code copied from yumcommands.py + cachemgr.populate_cache() + print(json.dumps(True)) + except yum.Errors.YumBaseError: + logger.error("Unexpected error creating cache: %s" % + sys.exc_info()[1], exc_info=1) + print(json.dumps(False)) elif cmd == "complete": - data = json.loads(sys.stdin.read()) - depsolver.groups = data['groups'] - (packages, unknown) = depsolver.complete([pkg_to_tuple(p) - for p in data['packages']]) - print(json.dumps(dict(packages=list(packages), - unknown=list(unknown)))) + depsolver = DepSolver(options.config, options.verbose) + try: + data = json.loads(sys.stdin.read()) + except: + logger.error("Unexpected error decoding JSON input: %s" % + sys.exc_info()[1]) + rv = 2 + try: + depsolver.groups = data['groups'] + (packages, unknown) = depsolver.complete( + [pkg_to_tuple(p) for p in data['packages']]) + print(json.dumps(dict(packages=list(packages), + unknown=list(unknown)))) + except: + logger.error("Unexpected error completing package set: %s" % + sys.exc_info()[1], exc_info=1) + print(json.dumps(dict(packages=[], unknown=data['packages']))) + rv = 2 elif cmd == "get_groups": - data = json.loads(sys.stdin.read()) - rv = dict() - for gdata in data: - if "type" in gdata: - packages = depsolver.get_group(gdata['group'], - ptype=gdata['type']) - else: - packages = depsolver.get_group(gdata['group']) - rv[gdata['group']] = list(packages) - print(json.dumps(rv)) - + depsolver = DepSolver(options.config, options.verbose) + try: + data = json.loads(sys.stdin.read()) + rv = dict() + for gdata in data: + if "type" in gdata: + packages = depsolver.get_group(gdata['group'], + ptype=gdata['type']) + else: + packages = depsolver.get_group(gdata['group']) + rv[gdata['group']] = list(packages) + print(json.dumps(rv)) + except: + logger.error("Unexpected error getting groups: %s" % + sys.exc_info()[1], exc_info=1) + print(json.dumps(dict())) + rv = 2 + else: + logger.error("Unknown command %s" % cmd) + print(json.dumps(None)) + rv = 2 + return rv if __name__ == '__main__': sys.exit(main()) |