summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael E Brown <mebrown@michaels-house.net>2007-10-21 05:00:27 -0500
committerMichael E Brown <mebrown@michaels-house.net>2007-10-21 05:00:27 -0500
commit03b315f0c466b577cd4c27cac2f645106b7b84fb (patch)
tree4d0eb1250098cb03b9ec374a98dbe21e5aa23f9a /src
parent15cd1d4715777110a86467a2f9ecf0e64bce91f9 (diff)
downloadmock-03b315f0c466b577cd4c27cac2f645106b7b84fb.tar.gz
mock-03b315f0c466b577cd4c27cac2f645106b7b84fb.tar.xz
mock-03b315f0c466b577cd4c27cac2f645106b7b84fb.zip
create plugin infrastructure. move all caching related stuff to plugins.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/mock.py41
-rw-r--r--src/py-libs/backend.py216
-rw-r--r--src/py-libs/plugins/ccache.py90
-rw-r--r--src/py-libs/plugins/root_cache.py107
-rw-r--r--src/py-libs/plugins/yum_cache.py106
5 files changed, 356 insertions, 204 deletions
diff --git a/src/mock.py b/src/mock.py
index 0970606..e44c928 100755
--- a/src/mock.py
+++ b/src/mock.py
@@ -49,7 +49,7 @@ log = logging.getLogger()
logging.basicConfig()
@traceLog(log)
-def command_parse():
+def command_parse(config_opts):
"""return options and args from parsing the command line"""
usage = """
@@ -89,10 +89,10 @@ def command_parse():
default=None, help="Fail build if rpmbuild takes longer than 'timeout' seconds ")
# caching
- parser.add_option("--enable-cache", action="append", dest="enabled_caches", type="string",
- default=[], help="Disable types of caching. Valid values: 'root_cache', 'yum_cache', 'ccache'")
- parser.add_option("--disable-cache", action="append", dest="disabled_caches", type="string",
- default=[], help="Disable types of caching. Valid values: 'root_cache', 'yum_cache', 'ccache'")
+ parser.add_option("--enable-plugin", action="append", dest="enabled_plugins", type="string",
+ default=[], help="Enable plugin. Currently-available plugins: %s" % repr(config_opts['plugins']))
+ parser.add_option("--disable-plugin", action="append", dest="disabled_plugins", type="string",
+ default=[], help="Disable plugin. Currently-available plugins: %s" % repr(config_opts['plugins']))
return parser.parse_args()
@@ -120,12 +120,16 @@ def setup_default_config_opts(config_opts):
# (global) caching-related config options
config_opts['cache_topdir'] = '/var/lib/mock/cache'
- config_opts['enable_ccache'] = True
- config_opts['ccache_opts'] = {'max_age_days': 15, 'max_cache_size': "4G"}
- config_opts['enable_yum_cache'] = True
- config_opts['yum_cache_opts'] = {'max_age_days': 15}
- config_opts['enable_root_cache'] = True
- config_opts['root_cache_opts'] = {'max_age_days': 15}
+ config_opts['plugins'] = ('ccache', 'yum_cache', 'root_cache')
+ config_opts['plugin_dir'] = os.path.join(PKGPYTHONDIR, "plugins")
+ config_opts['plugin_conf'] = {
+ 'enable_ccache': True,
+ 'ccache_opts': {'max_age_days': 15, 'max_cache_size': "4G"},
+ 'enable_yum_cache': True,
+ 'yum_cache_opts': {'max_age_days': 15},
+ 'enable_root_cache': True,
+ 'root_cache_opts': {'max_age_days': 15},
+ }
# dependent on guest OS
config_opts['use_host_resolv'] = True
@@ -154,14 +158,13 @@ def set_config_opts_per_cmdline(config_opts, options):
if options.rpmbuild_timeout is not None:
config_opts['rpmbuild_timeout'] = options.rpmbuild_timeout
- legalCacheOpts = ("yum_cache", "root_cache", "ccache")
- for i in options.disabled_caches:
- if i not in legalCacheOpts:
- raise mock.exception.BadCmdline("Bad option for '--disable-cache=%s'. Expecting one of: %s" % (i, legalCacheOpts))
+ for i in options.disabled_plugins:
+ if i not in config_opts['plugins']:
+ raise mock.exception.BadCmdline("Bad option for '--disable-plugins=%s'. Expecting one of: %s" % (i, config_opts['plugins']))
config_opts['enable_%s' % i] = False
- for i in options.enabled_caches:
- if i not in legalCacheOpts:
- raise mock.exception.BadCmdline("Bad option for '--enable-cache=%s'. Expecting one of: %s" % (i, legalCacheOpts))
+ for i in options.enabled_plugins:
+ if i not in config_opts['plugins']:
+ raise mock.exception.BadCmdline("Bad option for '--enable-plugins=%s'. Expecting one of: %s" % (i, config_opts['plugins']))
config_opts['enable_%s' % i] = True
if options.cleanup_after and not options.resultdir:
@@ -218,7 +221,7 @@ def main(retParams):
# defaults
config_opts = {}
setup_default_config_opts(config_opts)
- (options, args) = command_parse()
+ (options, args) = command_parse(config_opts)
# config path -- can be overridden on cmdline
config_path=MOCKCONFDIR
diff --git a/src/py-libs/backend.py b/src/py-libs/backend.py
index ea87655..542dad4 100644
--- a/src/py-libs/backend.py
+++ b/src/py-libs/backend.py
@@ -21,6 +21,7 @@
# python library imports
import fcntl
import glob
+import imp
import logging
import os
import shutil
@@ -79,15 +80,11 @@ class Root(object):
self.macros = config['macros']
self.more_buildreqs = config['more_buildreqs']
self.cache_topdir = config['cache_topdir']
+ self.cachedir = os.path.join(self.cache_topdir, self.sharedRootName)
- self.enable_ccache = config['enable_ccache']
- self.ccache_opts = config['ccache_opts']
-
- self.enable_yum_cache = config['enable_yum_cache']
- self.yum_cache_opts = config['yum_cache_opts']
-
- self.enable_root_cache = config['enable_root_cache']
- self.root_cache_opts = config['root_cache_opts']
+ self.plugins = config['plugins']
+ self.pluginConf = config['plugin_conf']
+ self.pluginDir = config['plugin_dir']
# mount/umount
self.umountCmds = ['umount -n %s/proc' % self.rootdir,
@@ -99,12 +96,21 @@ class Root(object):
'mount -n -t sysfs mock_chroot_sysfs %s/sys' % self.rootdir,
]
+ self.state("init plugins")
+ self._initPlugins()
+
# officially set state so it is logged
self.state("start")
# =============
# 'Public' API
# =============
+ @traceLog(moduleLog)
+ def addHook(self, stage, function):
+ hooks = self._hooks.get(stage, [])
+ if function not in hooks:
+ hooks.append(function)
+ self._hooks[stage] = hooks
@traceLog(moduleLog)
def state(self, newState = None):
@@ -150,6 +156,7 @@ class Root(object):
self.uidManager.becomeUser(0)
# create our base directory heirarchy
+ mock.util.mkdirIfAbsent(self.cachedir)
mock.util.mkdirIfAbsent(self.basedir)
mock.util.mkdirIfAbsent(self.rootdir)
@@ -170,8 +177,7 @@ class Root(object):
self.root_log.debug('rootdir = %s' % self.rootdir)
self.root_log.debug('resultdir = %s' % self.resultdir)
- # set up cache dirs:
- self._initCache()
+ # set up plugins:
self._callHooks('preinit')
# create skeleton dirs
@@ -389,181 +395,21 @@ class Root(object):
hook()
@traceLog(moduleLog)
- def _addHook(self, stage, function):
- hooks = self._hooks.get(stage, [])
- if function not in hooks:
- hooks.append(function)
- self._hooks[stage] = hooks
-
- @traceLog(moduleLog)
- def _initCache(self):
- self.cachedir = os.path.join(self.cache_topdir, self.sharedRootName)
- if self.enable_root_cache or self.enable_yum_cache or self.enable_ccache:
- mock.util.mkdirIfAbsent(self.cachedir)
-
- if self.enable_root_cache:
- self._setupRootCache()
-
- if self.enable_yum_cache:
- self._setupYumCache()
-
- if self.enable_ccache:
- self._setupCcache()
-
- @traceLog(moduleLog)
- def _rootCacheLock(self, shared=1):
- lockType = fcntl.LOCK_EX
- if shared: lockType = fcntl.LOCK_SH
- try:
- fcntl.lockf(self.rootCacheLock.fileno(), lockType | fcntl.LOCK_NB)
- except IOError, e:
- oldState = self.state()
- self.state("Waiting for rootcache lock")
- fcntl.lockf(self.rootCacheLock.fileno(), lockType)
- self.state(oldState)
-
- @traceLog(moduleLog)
- def _rootCacheUnlock(self):
- fcntl.lockf(self.rootCacheLock.fileno(), fcntl.LOCK_UN)
-
- @traceLog(moduleLog)
- def _rootCachePreInitHook(self):
- if os.path.exists(self.rootCacheFile):
- self.state("unpacking cache")
- self._rootCacheLock()
- mock.util.do("tar xzf %s -C %s" % (self.rootCacheFile, self.rootdir))
- self._rootCacheUnlock()
- self.chroot_setup_cmd = "update"
- self.chrootWasCleaned = False
-
- @traceLog(moduleLog)
- def _rootCachePostInitHook(self):
- # never rebuild cache unless it was a clean build.
- if self.chrootWasCleaned:
- self.state("creating cache")
- self._rootCacheLock(shared=0)
- mock.util.do("tar czf %s -C %s ." % (self.rootCacheFile, self.rootdir))
- self._rootCacheUnlock()
-
- @traceLog(moduleLog)
- def _setupRootCache(self):
- self._addHook("preinit", self._rootCachePreInitHook)
- self._addHook("postinit", self._rootCachePostInitHook)
- self.rootSharedCachePath = os.path.join(self.cachedir, "root_cache")
- self.rootCacheFile = os.path.join(self.rootSharedCachePath, "cache.tar.gz")
- mock.util.mkdirIfAbsent(self.rootSharedCachePath)
-
- # lock so others dont accidentally use root cache while we operate on it.
- self.rootCacheLock = open(os.path.join(self.rootSharedCachePath, "rootcache.lock"), "a+")
-
- # check cache age:
- self.state("enabling root cache")
- try:
- statinfo = os.stat(self.rootCacheFile)
- file_age_days = (time.time() - statinfo.st_ctime) / (60 * 60 * 24)
- if file_age_days > self.root_cache_opts['max_age_days']:
- os.unlink(self.rootCacheFile)
- except OSError:
- pass
-
- # lock the shared yum cache (when enabled) before any access
- # by yum, and prior to cleaning it. This prevents simultaneous access from
- # screwing things up. This can possibly happen, eg. when running multiple
- # mock instances with --uniqueext=
- @traceLog(moduleLog)
- def _yumCachePreYumHook(self):
- try:
- fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError, e:
- oldState = self.state()
- self.state("Waiting for yumcache lock")
- fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX)
- self.state(oldState)
-
- @traceLog(moduleLog)
- def _yumCachePostYumHook(self):
- fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_UN)
-
- @traceLog(moduleLog)
- def _setupYumCache(self):
- self._addHook("preyum", self._yumCachePreYumHook)
- self._addHook("postyum", self._yumCachePostYumHook)
- self.yumSharedCachePath = os.path.join(self.cachedir, "yum_cache")
- mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'var/cache/yum'))
- mock.util.mkdirIfAbsent(self.yumSharedCachePath)
- self.umountCmds.append('umount -n %s/var/cache/yum' % self.rootdir)
- self.mountCmds.append('mount -n --bind %s %s/var/cache/yum' % (self.yumSharedCachePath, self.rootdir))
-
- # lock so others dont accidentally use yum cache while we operate on it.
- self.yumCacheLock = open(os.path.join(self.yumSharedCachePath, "yumcache.lock"), "a+")
- self._yumCachePreYumHook()
-
- self.state("enabled yum cache, cleaning yum metadata")
- for (dirpath, dirnames, filenames) in os.walk(self.yumSharedCachePath):
- for filename in filenames:
- fullPath = os.path.join(dirpath, filename)
- statinfo = os.stat(fullPath)
- file_age_days = (time.time() - statinfo.st_ctime) / (60 * 60 * 24)
- # prune repodata so yum redownloads.
- # prevents certain errors where yum gets stuck due to bad metadata
- for ext in (".sqllite", ".xml", ".bz2", ".gz"):
- if filename.endswith(ext) and file_age_days > 1:
- os.unlink(fullPath)
- fullPath = None
- break
-
- if fullPath is None: continue
- if file_age_days > self.ccache_opts['max_age_days']:
- os.unlink(fullPath)
- continue
-
- self._yumCachePostYumHook()
-
- # set the max size before we actually use it during a build.
- # ccache itself manages size and settings.
- @traceLog(moduleLog)
- def _ccacheBuildHook(self):
- self.doChroot("ccache -M %s" % self.ccache_opts['max_cache_size'])
-
- # install ccache rpm after buildroot set up.
- @traceLog(moduleLog)
- def _ccachePostInitHook(self):
- #self.state("installing ccache")
- #self._yum('install ccache')
- self.preExistingDeps = "ccache"
-
- # basic idea here is that we add 'cc', 'gcc', 'g++' shell scripts to
- # to /tmp/ccache, which is bind-mounted from a shared location.
- # we then add this to the front of the path.
- # we also set a few admin variables used by ccache to find the shared
- # cache.
- @traceLog(moduleLog)
- def _setupCcache(self):
- self._addHook("prebuild", self._ccacheBuildHook)
- self._addHook("postinit", self._ccachePostInitHook)
- self.ccachePath = os.path.join(self.cachedir, "ccache")
- mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'tmp/ccache'))
- mock.util.mkdirIfAbsent(self.ccachePath)
- self.umountCmds.append('umount -n %s/tmp/ccache' % self.rootdir)
- self.mountCmds.append('mount -n --bind %s %s/tmp/ccache' % (self.ccachePath, self.rootdir))
- os.environ['PATH'] = "/tmp/ccache:%s" % (os.environ['PATH'])
- os.environ['CCACHE_DIR'] = "/tmp/ccache"
- os.environ['CCACHE_UMASK'] = "002"
- self._dumpToFile(os.path.join(self.ccachePath, "cc"),
- '#!/bin/sh\nexec ccache /usr/bin/cc "$@"\n', mode=0555)
- self._dumpToFile(os.path.join(self.ccachePath, "gcc"),
- '#!/bin/sh\nexec ccache /usr/bin/gcc "$@"\n', mode=0555)
- self._dumpToFile(os.path.join(self.ccachePath, "g++"),
- '#!/bin/sh\nexec ccache /usr/bin/g++ "$@"\n', mode=0555)
-
- @traceLog(moduleLog)
- def _dumpToFile(self, filename, contents, *args, **kargs):
- fd = open(filename, "w+")
- fd.write(contents)
- fd.close()
- mode = kargs.get("mode", None)
- if mode is not None:
- os.chmod(filename, mode)
+ def _initPlugins(self):
+ # Import plugins (simplified copy of what yum does). Can add yum
+ # features later when we prove we need them.
+ for modname, modulefile in [ (p, os.path.join(self.pluginDir, "%s.py" % p)) for p in self.plugins ]:
+ if not self.pluginConf.get("enable_%s"%modname): continue
+ fp, pathname, description = imp.find_module(modname, [self.pluginDir])
+ try:
+ module = imp.load_module(modname, fp, pathname, description)
+ finally:
+ fp.close()
+
+ if not hasattr(module, 'requires_api_version'):
+ raise mock.exception.Error('Plugin "%s" doesn\'t specify required API version' % modname)
+
+ module.init(self, self.pluginConf["%s_opts" % modname])
@traceLog(moduleLog)
def _mountall(self):
diff --git a/src/py-libs/plugins/ccache.py b/src/py-libs/plugins/ccache.py
new file mode 100644
index 0000000..f2f9d63
--- /dev/null
+++ b/src/py-libs/plugins/ccache.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python -tt
+# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Written by Seth Vidal
+# Sections taken from Mach by Thomas Vander Stichele
+# revised and adapted by Michael Brown
+
+# python library imports
+import logging
+import os
+
+# our imports
+from mock.trace_decorator import traceLog
+import mock.util
+
+# set up logging, module options
+moduleLog = logging.getLogger("mock")
+requires_api_version = "1.0"
+
+# plugin entry point
+def init(rootObj, conf):
+ ccache = CCache(rootObj, conf)
+
+# classes
+class CCache(object):
+ """enables ccache in buildroot/rpmbuild"""
+ @traceLog(moduleLog)
+ def __init__(self, rootObj, conf):
+ self.rootObj = rootObj
+ self.ccache_opts = conf
+ self.ccachePath = os.path.join(rootObj.cachedir, "ccache")
+ self.rootdir = rootObj.rootdir
+ rootObj.ccacheObj = self
+ rootObj.preExistingDeps = "ccache"
+ rootObj.addHook("prebuild", self._ccacheBuildHook)
+ rootObj.addHook("preinit", self._ccachePreInitHook)
+ rootObj.umountCmds.append('umount -n %s/tmp/ccache' % rootObj.rootdir)
+ rootObj.mountCmds.append('mount -n --bind %s %s/tmp/ccache' % (self.ccachePath, rootObj.rootdir))
+
+ # =============
+ # 'Private' API
+ # =============
+ # set the max size before we actually use it during a build.
+ # ccache itself manages size and settings.
+ @traceLog(moduleLog)
+ def _ccacheBuildHook(self):
+ self.rootObj.doChroot("ccache -M %s" % self.ccache_opts['max_cache_size'])
+
+ # basic idea here is that we add 'cc', 'gcc', 'g++' shell scripts to
+ # to /tmp/ccache, which is bind-mounted from a shared location.
+ # we then add this to the front of the path.
+ # we also set a few admin variables used by ccache to find the shared
+ # cache.
+ @traceLog(moduleLog)
+ def _ccachePreInitHook(self):
+ mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'tmp/ccache'))
+ mock.util.mkdirIfAbsent(self.ccachePath)
+ os.environ['PATH'] = "/tmp/ccache:%s" % (os.environ['PATH'])
+ os.environ['CCACHE_DIR'] = "/tmp/ccache"
+ os.environ['CCACHE_UMASK'] = "002"
+ self._dumpToFile(os.path.join(self.ccachePath, "cc"),
+ '#!/bin/sh\nexec ccache /usr/bin/cc "$@"\n', mode=0555)
+ self._dumpToFile(os.path.join(self.ccachePath, "gcc"),
+ '#!/bin/sh\nexec ccache /usr/bin/gcc "$@"\n', mode=0555)
+ self._dumpToFile(os.path.join(self.ccachePath, "g++"),
+ '#!/bin/sh\nexec ccache /usr/bin/g++ "$@"\n', mode=0555)
+
+ @traceLog(moduleLog)
+ def _dumpToFile(self, filename, contents, *args, **kargs):
+ fd = open(filename, "w+")
+ fd.write(contents)
+ fd.close()
+ mode = kargs.get("mode", None)
+ if mode is not None:
+ os.chmod(filename, mode)
+
+
diff --git a/src/py-libs/plugins/root_cache.py b/src/py-libs/plugins/root_cache.py
new file mode 100644
index 0000000..add7b19
--- /dev/null
+++ b/src/py-libs/plugins/root_cache.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python -tt
+# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Written by Seth Vidal
+# Sections taken from Mach by Thomas Vander Stichele
+# revised and adapted by Michael Brown
+
+# python library imports
+import fcntl
+import logging
+import os
+import time
+
+# our imports
+from mock.trace_decorator import traceLog
+import mock.util
+
+# set up logging, module options
+moduleLog = logging.getLogger("mock")
+requires_api_version = "1.0"
+
+# plugin entry point
+def init(rootObj, conf):
+ rootCache = RootCache(rootObj, conf)
+
+# classes
+class RootCache(object):
+ """caches root environment in a tarball"""
+ @traceLog(moduleLog)
+ def __init__(self, rootObj, conf):
+ self.rootObj = rootObj
+ self.root_cache_opts = conf
+ self.rootSharedCachePath = os.path.join(rootObj.cachedir, "root_cache")
+ self.rootCacheFile = os.path.join(self.rootSharedCachePath, "cache.tar.gz")
+ self.rootCacheLock = None
+ self.state = rootObj.state
+ self.rootdir = rootObj.rootdir
+ rootObj.rootCacheObj = self
+ rootObj.addHook("preinit", self._rootCachePreInitHook)
+ rootObj.addHook("postinit", self._rootCachePostInitHook)
+
+ # =============
+ # 'Private' API
+ # =============
+ @traceLog(moduleLog)
+ def _rootCacheLock(self, shared=1):
+ lockType = fcntl.LOCK_EX
+ if shared: lockType = fcntl.LOCK_SH
+ try:
+ fcntl.lockf(self.rootCacheLock.fileno(), lockType | fcntl.LOCK_NB)
+ except IOError, e:
+ oldState = self.state()
+ self.state("Waiting for rootcache lock")
+ fcntl.lockf(self.rootCacheLock.fileno(), lockType)
+ self.state(oldState)
+
+ @traceLog(moduleLog)
+ def _rootCacheUnlock(self):
+ fcntl.lockf(self.rootCacheLock.fileno(), fcntl.LOCK_UN)
+
+ @traceLog(moduleLog)
+ def _rootCachePreInitHook(self):
+ mock.util.mkdirIfAbsent(self.rootSharedCachePath)
+ # lock so others dont accidentally use root cache while we operate on it.
+ if self.rootCacheLock is None:
+ self.rootCacheLock = open(os.path.join(self.rootSharedCachePath, "rootcache.lock"), "a+")
+
+ # check cache age:
+ self.state("enabling root cache")
+ try:
+ statinfo = os.stat(self.rootCacheFile)
+ file_age_days = (time.time() - statinfo.st_ctime) / (60 * 60 * 24)
+ if file_age_days > self.root_cache_opts['max_age_days']:
+ os.unlink(self.rootCacheFile)
+ except OSError:
+ pass
+
+ if os.path.exists(self.rootCacheFile):
+ self.state("unpacking cache")
+ self._rootCacheLock()
+ mock.util.do("tar xzf %s -C %s" % (self.rootCacheFile, self.rootdir))
+ self._rootCacheUnlock()
+ self.chroot_setup_cmd = "update"
+ self.chrootWasCleaned = False
+
+ @traceLog(moduleLog)
+ def _rootCachePostInitHook(self):
+ # never rebuild cache unless it was a clean build.
+ if self.chrootWasCleaned:
+ self.state("creating cache")
+ self._rootCacheLock(shared=0)
+ mock.util.do("tar czf %s -C %s ." % (self.rootCacheFile, self.rootdir))
+ self._rootCacheUnlock()
+
diff --git a/src/py-libs/plugins/yum_cache.py b/src/py-libs/plugins/yum_cache.py
new file mode 100644
index 0000000..e375a69
--- /dev/null
+++ b/src/py-libs/plugins/yum_cache.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python -tt
+# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Written by Seth Vidal
+# Sections taken from Mach by Thomas Vander Stichele
+# revised and adapted by Michael Brown
+
+# python library imports
+import logging
+import fcntl
+import time
+import os
+
+# our imports
+from mock.trace_decorator import traceLog
+import mock.util
+
+# set up logging, module options
+moduleLog = logging.getLogger("mock")
+requires_api_version = "1.0"
+
+# plugin entry point
+def init(rootObj, conf):
+ yumCache = YumCache(rootObj, conf)
+
+# classes
+class YumCache(object):
+ """caches root environment in a tarball"""
+ @traceLog(moduleLog)
+ def __init__(self, rootObj, conf):
+ self.rootObj = rootObj
+ self.yum_cache_opts = conf
+ self.yumSharedCachePath = os.path.join(rootObj.cachedir, "yum_cache")
+ self.state = rootObj.state
+ self.rootdir = rootObj.rootdir
+ rootObj.yum_cacheObj = self
+ rootObj.addHook("preyum", self._yumCachePreYumHook)
+ rootObj.addHook("postyum", self._yumCachePostYumHook)
+ rootObj.addHook("preinit", self._yumCachePreInitHook)
+ rootObj.umountCmds.append('umount -n %s/var/cache/yum' % rootObj.rootdir)
+ rootObj.mountCmds.append('mount -n --bind %s %s/var/cache/yum' % (self.yumSharedCachePath, rootObj.rootdir))
+
+ # =============
+ # 'Private' API
+ # =============
+ # lock the shared yum cache (when enabled) before any access
+ # by yum, and prior to cleaning it. This prevents simultaneous access from
+ # screwing things up. This can possibly happen, eg. when running multiple
+ # mock instances with --uniqueext=
+ @traceLog(moduleLog)
+ def _yumCachePreYumHook(self):
+ try:
+ fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except IOError, e:
+ oldState = self.state()
+ self.state("Waiting for yumcache lock")
+ fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX)
+ self.state(oldState)
+
+ @traceLog(moduleLog)
+ def _yumCachePostYumHook(self):
+ fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_UN)
+
+ @traceLog(moduleLog)
+ def _yumCachePreInitHook(self):
+ mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'var/cache/yum'))
+ mock.util.mkdirIfAbsent(self.yumSharedCachePath)
+
+ # lock so others dont accidentally use yum cache while we operate on it.
+ self.yumCacheLock = open(os.path.join(self.yumSharedCachePath, "yumcache.lock"), "a+")
+ self._yumCachePreYumHook()
+
+ self.state("enabled yum cache, cleaning yum metadata")
+ for (dirpath, dirnames, filenames) in os.walk(self.yumSharedCachePath):
+ for filename in filenames:
+ fullPath = os.path.join(dirpath, filename)
+ statinfo = os.stat(fullPath)
+ file_age_days = (time.time() - statinfo.st_ctime) / (60 * 60 * 24)
+ # prune repodata so yum redownloads.
+ # prevents certain errors where yum gets stuck due to bad metadata
+ for ext in (".sqllite", ".xml", ".bz2", ".gz"):
+ if filename.endswith(ext) and file_age_days > 1:
+ os.unlink(fullPath)
+ fullPath = None
+ break
+
+ if fullPath is None: continue
+ if file_age_days > self.yum_cache_opts['max_age_days']:
+ os.unlink(fullPath)
+ continue
+
+ self._yumCachePostYumHook()
+