summaryrefslogtreecommitdiffstats
path: root/src/sbin/bcfg2-yum-helper
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbin/bcfg2-yum-helper')
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper161
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())