root/trunk/bcfg2/src/lib/Client/Tools/YUMng.py

Revision 5517, 15.6 KB (checked in by desai, 3 weeks ago)

YUMng: remove call to yum api that breaks plugin use (Resolves Ticket #765)

  • Property svn:revision set to Revision
Line 
1'''This provides bcfg2 support for yum'''
2__revision__ = '$Revision: $'
3
4import ConfigParser
5import copy
6import os.path
7import sys
8import yum
9import Bcfg2.Client.XML
10import Bcfg2.Client.Tools.RPMng
11
12# Fix for python2.3
13try:
14    set
15except NameError:
16    from sets import Set as set
17
18YAD = True
19CP = ConfigParser.ConfigParser()
20try:
21    if '-C' in sys.argv:
22        CP.read([sys.argv[sys.argv.index('-C') + 1]])
23    else:
24        CP.read(['/etc/bcfg2.conf'])
25    if CP.get('YUMng', 'autodep').lower() == 'false':
26        YAD = False
27except:
28    pass
29
30if not hasattr(Bcfg2.Client.Tools.RPMng, 'RPMng'):
31    raise ImportError
32
33def build_yname(pkgname, inst):
34    '''build yum appropriate package name'''
35    ypname = pkgname
36    if inst.get('version') != 'any':
37        ypname += '-'
38    if inst.get('epoch', False):
39        ypname += "%s:" % inst.get('epoch')
40    if inst.get('version', False) and inst.get('version') != 'any':
41        ypname += "%s" % (inst.get('version'))
42    if inst.get('release', False) and inst.get('release') != 'any':
43        ypname += "-%s" % (inst.get('release'))
44    if inst.get('arch', False) and inst.get('arch') != 'any':
45        ypname += ".%s" % (inst.get('arch'))
46    return ypname
47
48class YUMng(Bcfg2.Client.Tools.RPMng.RPMng):
49    '''Support for Yum packages'''
50    pkgtype = 'yum'
51
52    name = 'YUMng'
53    __execs__ = ['/usr/bin/yum', '/var/lib/rpm']
54    __handles__ = [('Package', 'yum'), ('Package', 'rpm')]
55
56    __req__ = {'Package': ['name', 'version']}
57    __ireq__ = {'Package': ['name']}
58    #__ireq__ = {'Package': ['name', 'version']}
59
60    __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']}
61    __new_ireq__ = {'Package': ['name'], \
62                    'Instance': []}
63    #__new_ireq__ = {'Package': ['name', 'uri'], \
64    #                'Instance': ['simplefile', 'version', 'release', 'arch']}
65
66    __gpg_req__ = {'Package': ['name', 'version']}
67    __gpg_ireq__ = {'Package': ['name', 'version']}
68
69    __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']}
70    __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']}
71
72    conflicts = ['RPMng']
73
74    def __init__(self, logger, setup, config):
75        Bcfg2.Client.Tools.RPMng.RPMng.__init__(self, logger, setup, config)
76        self.yum_avail = dict()
77        self.yum_installed = dict()
78        self.yb = yum.YumBase()
79        self.yb.doConfigSetup()
80        self.yb.doTsSetup()
81        self.yb.doRpmDBSetup()
82        yup = self.yb.doPackageLists(pkgnarrow='updates')
83        if hasattr(self.yb.rpmdb, 'pkglist'):
84            yinst = self.yb.rpmdb.pkglist
85        else:
86            yinst = self.yb.rpmdb.getPkgList()
87        for dest, source in [(self.yum_avail, yup.updates),
88                             (self.yum_installed, yinst)]:
89            for pkg in source:
90                if dest is self.yum_avail:
91                    pname = pkg.name
92                    data = [(pkg.arch, (pkg.epoch, pkg.version, pkg.release))]
93                else:
94                    pname = pkg[0]
95                    data = [(pkg[1], (pkg[2], pkg[3], pkg[4]))]
96                if pname in dest:
97                    dest[pname].update(data)
98                else:
99                    dest[pname] = dict(data)
100
101    def VerifyPackage(self, entry, modlist):
102        pinned_version = None
103        if entry.get('version', False) == 'auto':
104            # old style entry; synthesize Instances from current installed
105            if entry.get('name') not in self.yum_installed and \
106                   entry.get('name') not in self.yum_avail:
107                # new entry; fall back to default
108                entry.set('version', 'any')
109            else:
110                data = copy.copy(self.yum_installed[entry.get('name')])
111                if entry.get('name') in self.yum_avail:
112                    # installed but out of date
113                    data.update(self.yum_avail[entry.get('name')])
114                for (arch, (epoch, vers, rel)) in list(data.items()):
115                    x= Bcfg2.Client.XML.SubElement(entry, "Instance",
116                                                   name=entry.get('name'),
117                                                   version=vers, arch=arch,
118                                                   release=rel, epoch=epoch)
119                    if 'verify_flags' in entry.attrib:
120                        x.set('verify_flags', entry.get('verify_flags'))
121                    if 'verify' in entry.attrib:
122                        x.set('verify', entry.get('verify'))
123        return Bcfg2.Client.Tools.RPMng.RPMng.VerifyPackage(self, entry,
124                                                            modlist)
125
126    def Install(self, packages, states):
127        '''
128           Try and fix everything that RPMng.VerifyPackages() found wrong for
129           each Package Entry.  This can result in individual RPMs being
130           installed (for the first time), deleted, downgraded
131           or upgraded.
132
133           NOTE: YUM can not reinstall a package that it thinks is already
134                 installed.
135
136           packages is a list of Package Elements that has
137               states[<Package Element>] == False
138
139           The following effects occur:
140           - states{} is conditionally updated for each package.
141           - self.installed{} is rebuilt, possibly multiple times.
142           - self.instance_status{} is conditionally updated for each instance
143             of a package.
144           - Each package will be added to self.modified[] if its states{}
145             entry is set to True.
146        '''
147        self.logger.info('Running YUMng.Install()')
148
149        install_pkgs = []
150        gpg_keys = []
151        upgrade_pkgs = []
152
153        # Remove extra instances.
154        # Can not reverify because we don't have a package entry.
155        if len(self.extra_instances) > 0:
156            if (self.setup.get('remove') == 'all' or \
157                self.setup.get('remove') == 'packages'):
158                self.RemovePackages(self.extra_instances)
159            else:
160                self.logger.info("The following extra package instances will be removed by the '-r' option:")
161                for pkg in self.extra_instances:
162                    for inst in pkg:
163                        self.logger.info("    %s %s" % \
164                                         ((pkg.get('name'), self.str_evra(inst))))
165
166        # Figure out which instances of the packages actually need something
167        # doing to them and place in the appropriate work 'queue'.
168        for pkg in packages:
169            insts = [pinst for pinst in pkg \
170                     if pinst.tag in ['Instance', 'Package']]
171            if insts:
172                for inst in insts:
173                    if self.FixInstance(inst, self.instance_status[inst]):
174                        if self.instance_status[inst].get('installed', False) \
175                               == False:
176                            if pkg.get('name') == 'gpg-pubkey':
177                                gpg_keys.append(inst)
178                            else:
179                                install_pkgs.append(inst)
180                        elif self.instance_status[inst].get('version_fail', \
181                                                            False) == True:
182                            upgrade_pkgs.append(inst)
183            else:
184                install_pkgs.append(pkg)
185
186        # Install GPG keys.
187        # Alternatively specify the required keys using 'gpgkey' in the
188        # repository definition in yum.conf.  YUM will install the keys
189        # automatically.
190        if len(gpg_keys) > 0:
191            for inst in gpg_keys:
192                self.logger.info("Installing GPG keys.")
193                if inst.get('simplefile') is None:
194                    self.logger.error("GPG key has no simplefile attribute")
195                    continue
196                key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
197                                                     inst.get('simplefile'))
198                cmdrc, output = self.cmd.run("rpm --import %s" % key_arg)
199                if cmdrc != 0:
200                    self.logger.debug("Unable to install %s-%s" % \
201                                              (self.instance_status[inst].get('pkg').get('name'), \
202                                               self.str_evra(inst)))
203                else:
204                    self.logger.debug("Installed %s-%s-%s" % \
205                                              (self.instance_status[inst].get('pkg').get('name'), \
206                                               inst.get('version'), inst.get('release')))
207            self.RefreshPackages()
208            self.gpg_keyids = self.getinstalledgpg()
209            pkg = self.instance_status[gpg_keys[0]].get('pkg')
210            states[pkg] = self.VerifyPackage(pkg, [])
211
212        # Install packages.
213        if len(install_pkgs) > 0:
214            self.logger.info("Attempting to install packages")
215
216            if YAD:
217                pkgtool = "/usr/bin/yum -d0 -y install %s"
218            else:
219                pkgtool = "/usr/bin/yum -d0 install %s"
220
221            install_args = []
222            for inst in install_pkgs:
223                pkg_arg = self.instance_status[inst].get('pkg').get('name')
224                install_args.append(build_yname(pkg_arg, inst))
225
226            cmdrc, output = self.cmd.run(pkgtool % " ".join(install_args))
227            if cmdrc == 0:
228                # The yum command succeeded.  All packages installed.
229                self.logger.info("Single Pass for Install Succeeded")
230                self.RefreshPackages()
231            else:
232                # The yum command failed.  No packages installed.
233                # Try installing instances individually.
234                self.logger.error("Single Pass Install of Packages Failed")
235                installed_instances = []
236                for inst in install_pkgs:
237                    pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
238   
239                    cmdrc, output = self.cmd.run(pkgtool % pkg_arg)
240                    if cmdrc == 0:
241                        installed_instances.append(inst)
242                    else:
243                        self.logger.debug("%s %s would not install." % \
244                                              (self.instance_status[inst].get('pkg').get('name'), \
245                                               self.str_evra(inst)))
246                self.RefreshPackages()
247
248        # Fix upgradeable packages.
249        if len(upgrade_pkgs) > 0:
250            self.logger.info("Attempting to upgrade packages")
251
252            if YAD:
253                pkgtool = "/usr/bin/yum -d0 -y update %s"
254            else:
255                pkgtool = "/usr/bin/yum -d0 update %s"
256
257            upgrade_args = []
258            for inst in upgrade_pkgs:
259                pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
260                upgrade_args.append(pkg_arg)
261
262            cmdrc, output = self.cmd.run(pkgtool % " ".join(upgrade_args))
263            if cmdrc == 0:
264                # The yum command succeeded.  All packages installed.
265                self.logger.info("Single Pass for Install Succeeded")
266                self.RefreshPackages()
267            else:
268                # The yum command failed.  No packages installed.
269                # Try installing instances individually.
270                self.logger.error("Single Pass Install of Packages Failed")
271                installed_instances = []
272                for inst in upgrade_pkgs:
273                    pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst)
274                    cmdrc, output = self.cmd.run(pkgtool % pkg_arg)
275                    if cmdrc == 0:
276                        installed_instances.append(inst)
277                    else:
278                        self.logger.debug("%s %s would not install." % \
279                                              (self.instance_status[inst].get('pkg').get('name'), \
280                                               self.str_evra(inst)))
281
282                self.RefreshPackages()
283
284        if not self.setup['kevlar']:
285            for pkg_entry in [p for p in packages if self.canVerify(p)]:
286                self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name')))
287                states[pkg_entry] = self.VerifyPackage(pkg_entry, \
288                                                       self.modlists.get(pkg_entry, []))
289
290        for entry in [ent for ent in packages if states[ent]]:
291            self.modified.append(entry)
292
293    def RemovePackages(self, packages):
294        '''
295           Remove specified entries.
296
297           packages is a list of Package Entries with Instances generated
298           by FindExtraPackages().
299        '''
300        self.logger.debug('Running YUMng.RemovePackages()')
301
302        if YAD:
303            pkgtool = "/usr/bin/yum -d0 -y erase %s"
304        else:
305            pkgtool = "/usr/bin/yum -d0 erase %s"
306
307        erase_args = []
308        for pkg in packages:
309            for inst in pkg:
310                if pkg.get('name') != 'gpg-pubkey':
311                    pkg_arg = pkg.get('name') + '-'
312                    if inst.get('epoch', False):
313                        pkg_arg = pkg_arg + inst.get('epoch') + ':'
314                    pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release')
315                    if inst.get('arch', False):
316                        pkg_arg = pkg_arg + '.' + inst.get('arch')
317                    erase_args.append(pkg_arg)
318                else:
319                    pkgspec = { 'name':pkg.get('name'),
320                            'version':inst.get('version'),
321                            'release':inst.get('release')}
322                    self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
323                                                 % (pkgspec.get('name'), self.str_evra(pkgspec)))
324                    self.logger.info("         This package will be deleted in a future version of the RPMng driver.")
325
326        cmdrc, output = self.cmd.run(pkgtool % " ".join(erase_args))
327        if cmdrc == 0:
328            self.modified += packages
329            for pkg in erase_args:
330                self.logger.info("Deleted %s" % (pkg))
331        else:
332            self.logger.info("Bulk erase failed with errors:")
333            self.logger.debug("Erase results = %s" % output)
334            self.logger.info("Attempting individual erase for each package.")
335            for pkg in packages:
336                pkg_modified = False
337                for inst in pkg:
338                    if pkg.get('name') != 'gpg-pubkey':
339                        pkg_arg = pkg.get('name') + '-'
340                        if inst.attrib.has_key('epoch'):
341                            pkg_arg = pkg_arg + inst.get('epoch') + ':'
342                        pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release')
343                        if 'arch' in inst.attrib:
344                            pkg_arg = pkg_arg + '.' + inst.get('arch')
345                    else:
346                        self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
347                                                 % (pkg.get('name'), self.str_evra(pkg)))
348                        self.logger.info("         This package will be deleted in a future version of the RPMng driver.")
349                        continue
350
351                    cmdrc, output = self.cmd.run(self.pkgtool % pkg_arg)
352                    if cmdrc == 0:
353                        pkg_modified = True
354                        self.logger.info("Deleted %s" % pkg_arg)
355                    else:
356                        self.logger.error("unable to delete %s" % pkg_arg)
357                        self.logger.debug("Failure = %s" % output)
358                if pkg_modified == True:
359                    self.modified.append(pkg)
360
361
362        self.RefreshPackages()
363        self.extra = self.FindExtraPackages()
Note: See TracBrowser for help on using the browser.