1 | '''This is the bcfg2 support for apt-get''' |
---|
2 | __revision__ = '$Revision: 4923 $' |
---|
3 | |
---|
4 | import apt.cache |
---|
5 | import os, re |
---|
6 | import Bcfg2.Client.Tools |
---|
7 | |
---|
8 | class APT(Bcfg2.Client.Tools.Tool): |
---|
9 | '''The Debian toolset implements package and service operations and inherits |
---|
10 | the rest from Toolset.Toolset''' |
---|
11 | __name__ = 'APTng' |
---|
12 | __execs__ = ['/usr/bin/debsums', '/usr/bin/apt-get', '/usr/bin/dpkg'] |
---|
13 | __important__ = ["/etc/apt/sources.list", |
---|
14 | "/var/cache/debconf/config.dat", |
---|
15 | "/var/cache/debconf/templates.dat", |
---|
16 | '/etc/passwd', '/etc/group', |
---|
17 | '/etc/apt/apt.conf', '/etc/dpkg/dpkg.cfg'] |
---|
18 | __handles__ = [('Package', 'deb')] |
---|
19 | __req__ = {'Package': ['name', 'version']} |
---|
20 | pkgcmd = 'apt-get ' + \ |
---|
21 | '-o DPkg::Options::=--force-overwrite ' + \ |
---|
22 | '-o DPkg::Options::=--force-confold ' + \ |
---|
23 | '--reinstall ' + \ |
---|
24 | '-q=2 ' + \ |
---|
25 | '--force-yes ' + \ |
---|
26 | '-y install %s' |
---|
27 | |
---|
28 | def __init__(self, logger, cfg, setup): |
---|
29 | Bcfg2.Client.Tools.Tool.__init__(self, logger, cfg, setup) |
---|
30 | self.cfg = cfg |
---|
31 | os.environ["DEBIAN_FRONTEND"] = 'noninteractive' |
---|
32 | self.pkg_cache = apt.cache.Cache() |
---|
33 | self.actions = {} |
---|
34 | if self.setup['kevlar'] and not self.setup['dryrun']: |
---|
35 | self.cmd.run("dpkg --force-confold --configure --pending") |
---|
36 | self.cmd.run("apt-get clean") |
---|
37 | try: |
---|
38 | self.pkg_cache.update() |
---|
39 | except: |
---|
40 | self.logger.error("Failed to update apt cache") |
---|
41 | |
---|
42 | def FindExtra(self): |
---|
43 | '''Find extra packages''' |
---|
44 | packages = [entry.get('name') for entry in self.getSupportedEntries()] |
---|
45 | extras = [(p.name, p.installedVersion) for p in self.pkg_cache |
---|
46 | if p.isInstalled and p.name not in packages] |
---|
47 | return [Bcfg2.Client.XML.Element('Package', name=name, \ |
---|
48 | type='deb', version=version) \ |
---|
49 | for (name, version) in extras] |
---|
50 | |
---|
51 | def VerifyDebsums(self, entry, modlist): |
---|
52 | output = self.cmd.run("/usr/bin/debsums -as %s" % entry.get('name'))[1] |
---|
53 | if len(output) == 1 and "no md5sums for" in output[0]: |
---|
54 | self.logger.info("Package %s has no md5sums. Cannot verify" % \ |
---|
55 | entry.get('name')) |
---|
56 | entry.set('qtext', "Reinstall Package %s-%s to setup md5sums? (y/N) " \ |
---|
57 | % (entry.get('name'), entry.get('version'))) |
---|
58 | return False |
---|
59 | files = [] |
---|
60 | for item in output: |
---|
61 | if "checksum mismatch" in item: |
---|
62 | files.append(item.split()[-1]) |
---|
63 | elif "can't open" in item: |
---|
64 | files.append(item.split()[5]) |
---|
65 | elif "is not installed" in item: |
---|
66 | self.logger.error("Package %s is not fully installed" \ |
---|
67 | % entry.get('name')) |
---|
68 | else: |
---|
69 | self.logger.error("Got Unsupported pattern %s from debsums" \ |
---|
70 | % item) |
---|
71 | files.append(item) |
---|
72 | # We check if there is file in the checksum to do |
---|
73 | if files: |
---|
74 | # if files are found there we try to be sure our modlist is sane |
---|
75 | # with erroneous symlinks |
---|
76 | modlist = [os.path.realpath(filename) for filename in modlist] |
---|
77 | bad = [filename for filename in files if filename not in modlist] |
---|
78 | if bad: |
---|
79 | self.logger.info("Package %s failed validation. Bad files are:" % \ |
---|
80 | entry.get('name')) |
---|
81 | self.logger.info(bad) |
---|
82 | entry.set('qtext', |
---|
83 | "Reinstall Package %s-%s to fix failing files? (y/N) " % \ |
---|
84 | (entry.get('name'), entry.get('version'))) |
---|
85 | return False |
---|
86 | return True |
---|
87 | |
---|
88 | def VerifyPackage(self, entry, modlist, checksums=True): |
---|
89 | '''Verify package for entry''' |
---|
90 | if not entry.attrib.has_key('version'): |
---|
91 | self.logger.info("Cannot verify unversioned package %s" % |
---|
92 | (entry.attrib['name'])) |
---|
93 | return False |
---|
94 | pkgname = entry.get('name') |
---|
95 | if not self.pkg_cache.has_key(pkgname) \ |
---|
96 | or not self.pkg_cache[pkgname].isInstalled: |
---|
97 | self.logger.info("Package %s not installed" % (entry.get('name'))) |
---|
98 | entry.set('current_exists', 'false') |
---|
99 | return False |
---|
100 | |
---|
101 | pkg = self.pkg_cache[pkgname] |
---|
102 | if entry.get('version') == 'auto': |
---|
103 | if self.pkg_cache._depcache.IsUpgradable(pkg._pkg): |
---|
104 | desiredVersion = pkg.candidateVersion |
---|
105 | else: |
---|
106 | desiredVersion = pkg.installedVersion |
---|
107 | else: |
---|
108 | desiredVersion = entry.get('version') |
---|
109 | if desiredVersion != pkg.installedVersion: |
---|
110 | entry.set('current_version', pkg.installedVersion) |
---|
111 | entry.set('qtext', "Modify Package %s (%s -> %s)? (y/N) " % \ |
---|
112 | (entry.get('name'), entry.get('current_version'), |
---|
113 | entry.get('version'))) |
---|
114 | return False |
---|
115 | else: |
---|
116 | # version matches |
---|
117 | if not self.setup['quick'] and entry.get('verify', 'true') == 'true' \ |
---|
118 | and checksums: |
---|
119 | pkgsums = self.VerifyDebsums(entry, modlist) |
---|
120 | return pkgsums |
---|
121 | |
---|
122 | def Remove(self, packages): |
---|
123 | '''Deal with extra configuration detected''' |
---|
124 | pkgnames = " ".join([pkg.get('name') for pkg in packages]) |
---|
125 | if len(packages) > 0: |
---|
126 | self.logger.info('Removing packages:') |
---|
127 | self.logger.info(pkgnames) |
---|
128 | for pkg in pkgnames: |
---|
129 | self.pkg_cache[pkg].markDelete(purge=True) |
---|
130 | self.pkg_cache.commit() |
---|
131 | self.modified += packages |
---|
132 | self.extra = self.FindExtra() |
---|
133 | |
---|
134 | def Install(self, packages, states): |
---|
135 | # it looks like you can't install arbitrary versions of software |
---|
136 | # out of the pkg cache, we will still need to call apt-get |
---|
137 | ipkgs = [] |
---|
138 | for pkg in packages: |
---|
139 | if not self.pkg_cache.has_key(pkg.get('name')): |
---|
140 | self.logger.error("APT has no information about package %s" % (pkg.get('name'))) |
---|
141 | continue |
---|
142 | if pkg.get('version') == 'auto': |
---|
143 | ipkgs.append("%s=%s" % (pkg.get('name'), |
---|
144 | self.pkg_cache[pkg.get('name')].candidateVersion)) |
---|
145 | continue |
---|
146 | if pkg.get('version') in \ |
---|
147 | [p.VerStr for p in self.pkg_cache[pkg.get('name')]._pkg.VersionList]: |
---|
148 | ipkgs.append("%s=%s" % (pkg.get('name'), pkg.get('version'))) |
---|
149 | rc = self.cmd.run(self.pkgcmd % (" ".join(ipkgs)))[0] |
---|
150 | if rc: |
---|
151 | self.logger.error("APT command failed") |
---|
152 | self.pkg_cache = apt.cache.Cache() |
---|
153 | self.extra = self.FindExtra() |
---|
154 | for package in packages: |
---|
155 | states[package] = self.VerifyPackage(package, [], checksums=False) |
---|
156 | if states[package]: |
---|
157 | self.modified.append(package) |
---|