#!/usr/bin/env python '''This is the bcfg2 support for debian''' __revision__ = '$Revision$' from copy import deepcopy from os import environ, stat, system from popen2 import Popen4 import apt_pkg from Bcfg2.Client.Toolset import Toolset def Detect(): try: stat('/etc/debian_version') return True except OSError: return False class Debian(Toolset): __important__ = ["/etc/apt/sources.list", "/var/cache/debconf/config.dat", \ "/var/cache/debconf/templates.dat", '/etc/passwd', '/etc/group'] def __init__(self, cfg, setup): Toolset.__init__(self, cfg, setup) self.cfg = cfg #system("dpkg --configure -a") if not self.setup['build']: system("dpkg-reconfigure -f noninteractive debconf") system("apt-get -q=2 -y update") environ["DEBIAN_FRONTEND"] = 'noninteractive' self.Refresh() def Refresh(self): apt_pkg.init() self.cache = apt_pkg.GetCache() self.installed = {} for pkg in self.cache.Packages: if pkg.CurrentVer: self.installed[pkg.Name] = pkg.CurrentVer.VerStr # implement entry (Verify|Install) ops def VerifyService(self, entry): if entry.attrib['status'] == 'off': cmd = Popen4("/usr/sbin/update-rc.d -n -f %s remove" % (entry.attrib['name'])) num = 1 else: cmd = Popen4("/usr/sbin/update-rc.d -n -f %s remove" % (entry.attrib['name'])) num = 2 cstat = cmd.poll() output = '' while cstat == -1: output += cmd.fromchild.read() cstat = cmd.poll() >> 8 if len([x for x in output.split('\n') if x]) > num: return False return True def InstallService(self, entry): if entry.attrib['status'] == 'off': if self.setup['dryrun']: print "Disabling service %s" % (entry.attrib['name']) rc = 1 else: rc = system("update-rc.d -f %s remove" % entry.attrib['name']) else: if self.setup['dryrun']: print "Enabling service %s" % (entry.attrib['name']) rc = 1 else: rc = system("update-rc.d %s defaults" % (entry.attrib['name'])) if rc: return False return True def VerifyPackage(self, entry, modlist=[]): if self.installed.has_key(entry.attrib['name']): if self.installed[entry.attrib['name']] == entry.attrib['version']: if not self.setup['quick']: cmd = Popen4("debsums -s %s"%(entry.attrib['name'])) cstat = cmd.poll() output = cmd.fromchild.read() while cstat == -1: output += cmd.fromchild.read() cstat = cmd.poll() output = [x for x in output.split('\n') if x] if [x for x in output if x not in modlist]: return False return True return False def InstallPackage(self, entry): if not entry.attrib.has_key('version'): print "Package entry for %s is malformed" % (entry.attrib['name']) return False if self.setup['dryrun'] or self.setup['verbose']: print "Installing package %s %s" % (entry.attrib['name'], entry.attrib['version']) if self.setup['dryrun']: return False else: # queue package for bulk installation self.pkgtodo.append(entry) return False def Inventory(self): print "In Inventory::" Toolset.Inventory(self) self.pkgwork = {'add':[], 'update':[], 'remove':[]} all = deepcopy(self.installed) desired = {} for entry in self.cfg.findall(".//Package"): desired[entry.attrib['name']] = entry for pkg, entry in desired.iteritems(): if self.states.get(entry, True): # package entry verifies del all[pkg] else: if all.has_key(pkg): # wrong version self.pkgwork['update'].append(entry) else: # new pkg self.pkgwork['add'].append(entry) # pkgwork contains all one-way verification data now # all data remaining in all is extra packages def Install(self): print "Installing" cmd = "apt-get --reinstall -q=2 -y install %s" print "Need to remove:", self.pkgwork['remove'] print "%s new, %s update, %s remove" % (len(self.pkgwork['add']), len(self.pkgwork['update']), len(self.pkgwork['remove'])) # try single large install rc = system(cmd % " ".join(["%s=%s" % (x.attrib['name'], x.attrib.get('version', 'dummy')) for x in self.pkgwork['add'] + self.pkgwork['update']])) if rc == 0: # set installed to true for pkgtodo for pkg in self.pkgwork['add'] + self.pkgwork['update']: self.states[pkg] = True self.pkgtodo = [] self.Refresh() else: # do single pass installs system("dpkg --configure --pending") self.Refresh() work = self.pkgwork['add'] + self.pkgwork['update'] for pkg in work: if self.VerifyPackage(pkg): self.states[pkg] = True #self.pkgtodo.remove(pkg) oldlen = len(work) + 1 while (len(work) < oldlen): oldlen = len(work) for pkg in work: print cmd % ("%s=%s"%(pkg.attrib['name'], pkg.attrib['version'])) rc = system(cmd % ("%s=%s" % (pkg.attrib['name'], pkg.attrib['version']))) if rc == 0: self.states[pkg] = True work.remove(pkg) else: print "Failed to install package %s" % (pkg.attrib['name']) for entry in [x for x in self.states if not self.states[x] and x.tag != 'Package']: self.InstallEntry(entry)