From af0c8a43ca7d5279afa076bb45b59c708b5f6863 Mon Sep 17 00:00:00 2001 From: Narayan Desai Date: Sat, 22 Nov 2008 18:05:11 +0000 Subject: New APT driver (git-svn sent the last change astray) git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@4981 ce84e21b-d406-0410-9b95-82705330c041 --- src/lib/Client/Tools/APT.py | 202 ++++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Client/Tools/APT.py index d73ed06dc..df11c81fc 100644 --- a/src/lib/Client/Tools/APT.py +++ b/src/lib/Client/Tools/APT.py @@ -1,11 +1,11 @@ '''This is the bcfg2 support for apt-get''' __revision__ = '$Revision$' -import apt_pkg +import apt.cache import os, re import Bcfg2.Client.Tools -class APT(Bcfg2.Client.Tools.PkgTool): +class APT(Bcfg2.Client.Tools.Tool): '''The Debian toolset implements package and service operations and inherits the rest from Toolset.Toolset''' __name__ = 'APT' @@ -17,103 +17,141 @@ class APT(Bcfg2.Client.Tools.PkgTool): '/etc/apt/apt.conf', '/etc/dpkg/dpkg.cfg'] __handles__ = [('Package', 'deb')] __req__ = {'Package': ['name', 'version']} - pkgtype = 'deb' - pkgtool = ('apt-get ' - '-o DPkg::Options::=--force-overwrite ' - '-o DPkg::Options::=--force-confold ' - '--reinstall ' - '-q=2 ' - '--force-yes ' - '-y install %s', - ('%s=%s', ['name', 'version'])) + pkgcmd = 'apt-get ' + \ + '-o DPkg::Options::=--force-overwrite ' + \ + '-o DPkg::Options::=--force-confold ' + \ + '--reinstall ' + \ + '-q=2 ' + \ + '--force-yes ' + \ + '-y install %s' - svcre = re.compile("/etc/.*/[SK]\d\d(?P\S+)") - def __init__(self, logger, cfg, setup): - Bcfg2.Client.Tools.PkgTool.__init__(self, logger, cfg, setup) + Bcfg2.Client.Tools.Tool.__init__(self, logger, cfg, setup) self.cfg = cfg os.environ["DEBIAN_FRONTEND"] = 'noninteractive' - self.updated = False + self.pkg_cache = apt.cache.Cache() + self.actions = {} + if self.setup['kevlar'] and not self.setup['dryrun']: + self.cmd.run("dpkg --force-confold --configure --pending") + self.cmd.run("apt-get clean") + try: + self.pkg_cache.update() + except: + self.logger.error("Failed to update apt cache") - def RefreshPackages(self): - '''Refresh memory hashes of packages''' - apt_pkg.init() - cache = apt_pkg.GetCache() - self.installed = {} - for pkg in cache.Packages: - if pkg.CurrentVer: - self.installed[pkg.Name] = pkg.CurrentVer.VerStr - self.extra = self.FindExtraPackages() + def FindExtra(self): + '''Find extra packages''' + packages = [entry.get('name') for entry in self.getSupportedEntries()] + extras = [(p.name, p.installedVersion) for p in self.pkg_cache + if p.isInstalled and p.name not in packages] + return [Bcfg2.Client.XML.Element('Package', name=name, \ + type='deb', version=version) \ + for (name, version) in extras] + + def VerifyDebsums(self, entry, modlist): + output = self.cmd.run("/usr/bin/debsums -as %s" % entry.get('name'))[1] + if len(output) == 1 and "no md5sums for" in output[0]: + self.logger.info("Package %s has no md5sums. Cannot verify" % \ + entry.get('name')) + entry.set('qtext', "Reinstall Package %s-%s to setup md5sums? (y/N) " \ + % (entry.get('name'), entry.get('version'))) + return False + files = [] + for item in output: + if "checksum mismatch" in item: + files.append(item.split()[-1]) + elif "can't open" in item: + files.append(item.split()[5]) + elif "is not installed" in item: + self.logger.error("Package %s is not fully installed" \ + % entry.get('name')) + else: + self.logger.error("Got Unsupported pattern %s from debsums" \ + % item) + files.append(item) + # We check if there is file in the checksum to do + if files: + # if files are found there we try to be sure our modlist is sane + # with erroneous symlinks + modlist = [os.path.realpath(filename) for filename in modlist] + bad = [filename for filename in files if filename not in modlist] + if bad: + self.logger.info("Package %s failed validation. Bad files are:" % \ + entry.get('name')) + self.logger.info(bad) + entry.set('qtext', + "Reinstall Package %s-%s to fix failing files? (y/N) " % \ + (entry.get('name'), entry.get('version'))) + return False + return True - def VerifyPackage(self, entry, modlist): + def VerifyPackage(self, entry, modlist, checksums=True): '''Verify package for entry''' if not entry.attrib.has_key('version'): self.logger.info("Cannot verify unversioned package %s" % (entry.attrib['name'])) return False - if self.installed.has_key(entry.attrib['name']): - if self.installed[entry.attrib['name']] == entry.attrib['version']: - if not self.setup['quick'] and entry.get('verify', 'true') == 'true': - output = self.cmd.run("/usr/bin/debsums -as %s" % entry.get('name'))[1] - if len(output) == 1 and "no md5sums for" in output[0]: - self.logger.info("Package %s has no md5sums. Cannot verify" % \ - entry.get('name')) - entry.set('qtext', "Reinstall Package %s-%s to setup md5sums? (y/N) " \ - % (entry.get('name'), entry.get('version'))) - return False - files = [] - for item in output: - if "checksum mismatch" in item: - files.append(item.split()[-1]) - elif "can't open" in item: - files.append(item.split()[5]) - elif "is not installed" in item: - self.logger.error("Package %s is not fully installed" \ - % entry.get('name')) - else: - self.logger.error("Got Unsupported pattern %s from debsums" \ - % item) - files.append(item) - # We check if there is file in the checksum to do - if files: - # if files are found there we try to be sure our modlist is sane - # with erroneous symlinks - modlist = [os.path.realpath(filename) for filename in modlist] - bad = [filename for filename in files if filename not in modlist] - if bad: - self.logger.info("Package %s failed validation. Bad files are:" % \ - entry.get('name')) - self.logger.info(bad) - entry.set('qtext', - "Reinstall Package %s-%s to fix failing md5sums? (y/N) " % \ - (entry.get('name'), entry.get('version'))) - return False - return True + pkgname = entry.get('name') + if not self.pkg_cache.has_key(pkgname) \ + or not self.pkg_cache[pkgname].isInstalled: + self.logger.info("Package %s not installed" % (entry.get('name'))) + entry.set('current_exists', 'false') + return False + + pkg = self.pkg_cache[pkgname] + if entry.get('version') == 'auto': + if self.pkg_cache._depcache.IsUpgradable(pkg._pkg): + desiredVersion = pkg.candidateVersion else: - entry.set('current_version', self.installed[entry.get('name')]) - entry.set('qtext', "Upgrade/downgrade Package %s (%s -> %s)? (y/N) " % \ - (entry.get('name'), entry.get('current_version'), - entry.get('version'))) - return False - self.logger.info("Package %s not installed" % (entry.get('name'))) - entry.set('current_exists', 'false') - return False + desiredVersion = pkg.installedVersion + else: + desiredVersion = entry.get('version') + if desiredVersion != pkg.installedVersion: + entry.set('current_version', pkg.installedVersion) + entry.set('qtext', "Modify Package %s (%s -> %s)? (y/N) " % \ + (entry.get('name'), entry.get('current_version'), + entry.get('version'))) + return False + else: + # version matches + if not self.setup['quick'] and entry.get('verify', 'true') == 'true' \ + and checksums: + pkgsums = self.VerifyDebsums(entry, modlist) + return pkgsums - def RemovePackages(self, packages): + def Remove(self, packages): '''Deal with extra configuration detected''' pkgnames = " ".join([pkg.get('name') for pkg in packages]) if len(packages) > 0: self.logger.info('Removing packages:') self.logger.info(pkgnames) - if self.cmd.run("apt-get remove --purge -y --force-yes %s" % pkgnames)[0] == 0: - self.modified += packages - self.RefreshPackages() - self.extra = self.FindExtraPackages() + for pkg in pkgnames: + self.pkg_cache[pkg].markDelete(purge=True) + self.pkg_cache.commit() + self.modified += packages + self.extra = self.FindExtra() def Install(self, packages, states): - if self.setup['kevlar'] and not self.setup['dryrun'] and not self.updated: - self.cmd.run("dpkg --force-confold --configure --pending") - self.cmd.run("apt-get clean") - self.cmd.run("apt-get -q=2 -y update") - self.updated = True - Bcfg2.Client.Tools.PkgTool.Install(self, packages, states) + # it looks like you can't install arbitrary versions of software + # out of the pkg cache, we will still need to call apt-get + ipkgs = [] + for pkg in packages: + if not self.pkg_cache.has_key(pkg.get('name')): + self.logger.error("APT has no information about package %s" % (pkg.get('name'))) + continue + if pkg.get('version') == 'auto': + ipkgs.append("%s=%s" % (pkg.get('name'), + self.pkg_cache[pkg.get('name')].candidateVersion)) + continue + if pkg.get('version') in \ + [p.VerStr for p in self.pkg_cache[pkg.get('name')]._pkg.VersionList]: + ipkgs.append("%s=%s" % (pkg.get('name'), pkg.get('version'))) + rc = self.cmd.run(self.pkgcmd % (" ".join(ipkgs)))[0] + if rc: + self.logger.error("APT command failed") + self.pkg_cache = apt.cache.Cache() + self.extra = self.FindExtra() + for package in packages: + states[package] = self.VerifyPackage(package, [], checksums=False) + if states[package]: + self.modified.append(package) -- cgit v1.2.3-1-g7c22