From 2217fa6295070f137988006c4bb00d25dfc0cb5e Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Mon, 15 Jul 2013 15:32:21 -0400 Subject: Read-only yum cache This makes the yum cache read-only so that bcfg2-yum-helper cannot update the cache on the fly, which should help avoid locking issues with the yum caches that can cause client runs to fail. It also makes the Packages plugin behave more consistently, since use of yum libraries won't cause the cache to be refreshed at random times on the fly, but rather more predictably as with the Apt cache or the yum cache without using yum libraries. Unlike those two cases, though, the caches will not all be downloaded initially, but rather opportunistically as needed. In order for this to work, the Bcfg2 server must not run as root. Root ignores the 'w' permissions bit, so the cache cannot be made read-only. --- src/sbin/bcfg2-yum-helper | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/sbin/bcfg2-yum-helper') diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper index 4ef531d39..473214d89 100755 --- a/src/sbin/bcfg2-yum-helper +++ b/src/sbin/bcfg2-yum-helper @@ -193,6 +193,20 @@ class DepSolver(object): if not msg.startswith("0 "): self.logger.info(msg) + 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 + self.yumbase.repos.populateSack(mdtype='metadata', cacheonly=1) + self.yumbase.repos.populateSack(mdtype='filelists', cacheonly=1) + self.yumbase.repos.populateSack(mdtype='otherdata', cacheonly=1) + def main(): parser = OptionParser() @@ -233,6 +247,15 @@ def main(): sys.exc_info()[1], exc_info=1) print(json.dumps(False)) rv = 2 + elif cmd == "makecache": + try: + # this code copied from yumcommands.py + depsolver.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": try: data = json.loads(sys.stdin.read()) -- cgit v1.2.3-1-g7c22 From 2cfbdf19d62880eed3e80f526bf12f6f98ff6633 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 18 Jul 2013 08:56:32 -0400 Subject: Packages: fixed read-only yum cache Replaced incredibly stupid (mea culpa!) and race-condition-prone system that toggled filesystem permissions (what was I thinking?!?) with judicious application of the yum cacheonly option. --- src/sbin/bcfg2-yum-helper | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'src/sbin/bcfg2-yum-helper') diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper index 473214d89..414606abb 100755 --- a/src/sbin/bcfg2-yum-helper +++ b/src/sbin/bcfg2-yum-helper @@ -42,8 +42,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 +57,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 +191,14 @@ class DepSolver(object): packages.add(txmbr.pkgtup) return list(packages), list(unknown) + +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 clean_cache(self): """ clean the yum cache """ for mdtype in ["Headers", "Packages", "Sqlite", "Metadata", @@ -237,10 +255,10 @@ def main(): # pylint: disable=W0702 rv = 0 - depsolver = DepSolver(options.config, options.verbose) if cmd == "clean": + cachemgr = CacheManager(options.config, options.verbose) try: - depsolver.clean_cache() + cachemgr.clean_cache() print(json.dumps(True)) except: logger.error("Unexpected error cleaning cache: %s" % @@ -248,15 +266,17 @@ def main(): print(json.dumps(False)) rv = 2 elif cmd == "makecache": + cachemgr = CacheManager(options.config, options.verbose) try: # this code copied from yumcommands.py - depsolver.populate_cache() + 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": + depsolver = DepSolver(options.config, options.verbose) try: data = json.loads(sys.stdin.read()) except: @@ -275,6 +295,7 @@ def main(): print(json.dumps(dict(packages=[], unknown=data['packages']))) rv = 2 elif cmd == "get_groups": + depsolver = DepSolver(options.config, options.verbose) try: data = json.loads(sys.stdin.read()) rv = dict() -- cgit v1.2.3-1-g7c22