diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Bcfg2/Client/Frame.py | 15 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/RPM.py | 988 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/RPMng.py | 989 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/YUM.py | 951 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/YUM24.py | 35 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/YUMng.py | 955 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/__init__.py | 3 | ||||
-rw-r--r-- | src/lib/Bcfg2/Options.py | 130 |
8 files changed, 2049 insertions, 2017 deletions
diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py index ef61940eb..b0032057a 100644 --- a/src/lib/Bcfg2/Client/Frame.py +++ b/src/lib/Bcfg2/Client/Frame.py @@ -84,11 +84,11 @@ class Frame(object): for tool in list(tclass.values()): try: self.tools.append(tool(self.logger, setup, config)) - except Bcfg2.Client.Tools.toolInstantiationError: + except Bcfg2.Client.Tools.ToolInstantiationError: continue except: - self.logger.error("Failed to instantiate tool %s" % \ - (tool), exc_info=1) + self.logger.error("Failed to instantiate tool %s" % tool, + exc_info=1) for tool in self.tools[:]: for conflict in getattr(tool, 'conflicts', []): @@ -99,10 +99,15 @@ class Frame(object): self.logger.info("Loaded tool drivers:") self.logger.info([tool.name for tool in self.tools]) + deprecated = [tool.name for tool in self.tools if tool.deprecated] + if deprecated: + self.logger.warning("Loaded deprecated tool drivers:") + self.logger.warning(deprecated) + # find entries not handled by any tools self.unhandled = [entry for struct in config - for entry in struct - if entry not in self.handled] + for entry in struct + if entry not in self.handled] if self.unhandled: self.logger.error("The following entries are not handled by any " diff --git a/src/lib/Bcfg2/Client/Tools/RPM.py b/src/lib/Bcfg2/Client/Tools/RPM.py new file mode 100644 index 000000000..1e54dc449 --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/RPM.py @@ -0,0 +1,988 @@ +"""Bcfg2 Support for RPMS""" + +import os.path +import rpm +import rpmtools +import Bcfg2.Client.Tools + +class RPM(Bcfg2.Client.Tools.PkgTool): + """Support for RPM packages.""" + __execs__ = ['/bin/rpm', '/var/lib/rpm'] + __handles__ = [('Package', 'rpm')] + + __req__ = {'Package': ['name', 'version']} + __ireq__ = {'Package': ['url']} + + __new_req__ = {'Package': ['name'], + 'Instance': ['version', 'release', 'arch']} + __new_ireq__ = {'Package': ['uri'], \ + 'Instance': ['simplefile']} + + __gpg_req__ = {'Package': ['name', 'version']} + __gpg_ireq__ = {'Package': ['name', 'version']} + + __new_gpg_req__ = {'Package': ['name'], + 'Instance': ['version', 'release']} + __new_gpg_ireq__ = {'Package': ['name'], + 'Instance': ['version', 'release']} + + conflicts = ['RPMng'] + + pkgtype = 'rpm' + pkgtool = ("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"])) + + def __init__(self, logger, setup, config): + Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) + + # create a global ignore list used when ignoring particular + # files during package verification + self.ignores = [entry.get('name') for struct in config for entry in struct \ + if entry.get('type') == 'ignore'] + self.instance_status = {} + self.extra_instances = [] + self.modlists = {} + self.gpg_keyids = self.getinstalledgpg() + + opt_prefix = self.name.lower() + self.installOnlyPkgs = self.setup["%s_installonly" % opt_prefix] + if 'gpg-pubkey' not in self.installOnlyPkgs: + self.installOnlyPkgs.append('gpg-pubkey') + self.erase_flags = self.setup['%s_erase_flags' % opt_prefix] + self.pkg_checks = self.setup['%s_pkg_checks' % opt_prefix] + self.pkg_verify = self.setup['%s_pkg_verify' % opt_prefix] + self.installed_action = self.setup['%s_installed_action' % opt_prefix] + self.version_fail_action = self.setup['%s_version_fail_action' % + opt_prefix] + self.verify_fail_action = self.setup['%s_verify_fail_action' % + opt_prefix] + self.verify_flags = self.setup['%s_verify_flags' % opt_prefix] + if '' in self.verify_flags: + self.verify_flags.remove('') + + self.logger.debug('%s: installOnlyPackages = %s' % + (self.name, self.installOnlyPkgs)) + self.logger.debug('%s: erase_flags = %s' % + (self.name, self.erase_flags)) + self.logger.debug('%s: pkg_checks = %s' % + (self.name, self.pkg_checks)) + self.logger.debug('%s: pkg_verify = %s' % + (self.name, self.pkg_verify)) + self.logger.debug('%s: installed_action = %s' % + (self.name, self.installed_action)) + self.logger.debug('%s: version_fail_action = %s' % + (self.name, self.version_fail_action)) + self.logger.debug('%s: verify_fail_action = %s' % + (self.name, self.verify_fail_action)) + self.logger.debug('%s: verify_flags = %s' % + (self.name, self.verify_flags)) + + # Force a re- prelink of all packages if prelink exists. + # Many, if not most package verifies can be caused by out of + # date prelinking. + if os.path.isfile('/usr/sbin/prelink') and not self.setup['dryrun']: + cmdrc, output = self.cmd.run('/usr/sbin/prelink -a -mR') + if cmdrc == 0: + self.logger.debug('Pre-emptive prelink succeeded') + else: + # FIXME : this is dumb - what if the output is huge? + self.logger.error('Pre-emptive prelink failed: %s' % output) + + + def RefreshPackages(self): + """ + Creates self.installed{} which is a dict of installed packages. + + The dict items are lists of nevra dicts. This loosely matches the + config from the server and what rpmtools uses to specify pacakges. + + e.g. + + self.installed['foo'] = [ {'name':'foo', 'epoch':None, + 'version':'1', 'release':2, + 'arch':'i386'}, + {'name':'foo', 'epoch':None, + 'version':'1', 'release':2, + 'arch':'x86_64'} ] + """ + self.installed = {} + refresh_ts = rpmtools.rpmtransactionset() + # Don't bother with signature checks at this stage. The GPG keys might + # not be installed. + refresh_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) + for nevra in rpmtools.rpmpackagelist(refresh_ts): + self.installed.setdefault(nevra['name'], []).append(nevra) + if self.setup['debug']: + print("The following package instances are installed:") + for name, instances in list(self.installed.items()): + self.logger.debug(" " + name) + for inst in instances: + self.logger.debug(" %s" %self.str_evra(inst)) + refresh_ts.closeDB() + del refresh_ts + + def VerifyPackage(self, entry, modlist, pinned_version=None): + """ + Verify Package status for entry. + Performs the following: + - Checks for the presence of required Package Instances. + - Compares the evra 'version' info against self.installed{}. + - RPM level package verify (rpm --verify). + - Checks for the presence of unrequired package instances. + + Produces the following dict and list for RPM.Install() to use: + For installs/upgrades/fixes of required instances: + instance_status = { <Instance Element Object>: + { 'installed': True|False, + 'version_fail': True|False, + 'verify_fail': True|False, + 'pkg': <Package Element Object>, + 'modlist': [ <filename>, ... ], + 'verify' : [ <rpm --verify results> ] + }, ...... + } + + For deletions of unrequired instances: + extra_instances = [ <Package Element Object>, ..... ] + + Constructs the text prompts for interactive mode. + """ + instances = [inst for inst in entry if inst.tag == 'Instance' or inst.tag == 'Package'] + if instances == []: + # We have an old style no Instance entry. Convert it to new style. + instance = Bcfg2.Client.XML.SubElement(entry, 'Package') + for attrib in list(entry.attrib.keys()): + instance.attrib[attrib] = entry.attrib[attrib] + if (self.pkg_checks and + entry.get('pkg_checks', 'true').lower() == 'true'): + if 'any' in [entry.get('version'), pinned_version]: + version, release = 'any', 'any' + elif entry.get('version') == 'auto': + if pinned_version != None: + version, release = pinned_version.split('-') + else: + return False + else: + version, release = entry.get('version').split('-') + instance.set('version', version) + instance.set('release', release) + if entry.get('verify', 'true') == 'false': + instance.set('verify', 'false') + instances = [ instance ] + + self.logger.debug("Verifying package instances for %s" % entry.get('name')) + package_fail = False + qtext_versions = '' + + if entry.get('name') in self.installed: + # There is at least one instance installed. + if (self.pkg_checks and + entry.get('pkg_checks', 'true').lower() == 'true'): + rpmTs = rpm.TransactionSet() + rpmHeader = None + for h in rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')): + if rpmHeader is None or rpm.versionCompare(h, rpmHeader) > 0: + rpmHeader = h + rpmProvides = [ h['provides'] for h in \ + rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')) ] + rpmIntersection = set(rpmHeader['provides']) & \ + set(self.installOnlyPkgs) + if len(rpmIntersection) > 0: + # Packages that should only be installed or removed. + # e.g. kernels. + self.logger.debug(" Install only package.") + for inst in instances: + self.instance_status.setdefault(inst, {})['installed'] = False + self.instance_status[inst]['version_fail'] = False + if inst.tag == 'Package' and len(self.installed[entry.get('name')]) > 1: + self.logger.error("WARNING: Multiple instances of package %s are installed." % \ + (entry.get('name'))) + for pkg in self.installed[entry.get('name')]: + if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) \ + or self.inst_evra_equal(inst, pkg): + if inst.get('version') == 'any': + self.logger.error("got any version") + self.logger.debug(" %s" % self.str_evra(inst)) + self.instance_status[inst]['installed'] = True + + if (self.pkg_verify and + inst.get('pkg_verify', 'true').lower() == 'true'): + flags = inst.get('verify_flags', '').split(',') + self.verify_flags + if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ + entry.get('name') != 'gpg-pubkey': + flags += ['nosignature', 'nodigest'] + self.logger.debug('WARNING: Package %s %s requires GPG Public key with ID %s'\ + % (pkg.get('name'), self.str_evra(pkg), \ + pkg.get('gpgkeyid', ''))) + self.logger.debug(' Disabling signature check.') + + if self.setup.get('quick', False): + if rpmtools.prelink_exists: + flags += ['nomd5', 'nosize'] + else: + flags += ['nomd5'] + self.logger.debug(" verify_flags = %s" % flags) + + if inst.get('verify', 'true') == 'false': + self.instance_status[inst]['verify'] = None + else: + vp_ts = rpmtools.rpmtransactionset() + self.instance_status[inst]['verify'] = \ + rpmtools.rpm_verify( vp_ts, pkg, flags) + vp_ts.closeDB() + del vp_ts + + if self.instance_status[inst]['installed'] == False: + self.logger.info(" Package %s %s not installed." % \ + (entry.get('name'), self.str_evra(inst))) + + qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) + entry.set('current_exists', 'false') + else: + # Normal Packages that can be upgraded. + for inst in instances: + self.instance_status.setdefault(inst, {})['installed'] = False + self.instance_status[inst]['version_fail'] = False + + # Only installed packages with the same architecture are + # relevant. + if inst.get('arch', None) == None: + arch_match = self.installed[entry.get('name')] + else: + arch_match = [pkg for pkg in self.installed[entry.get('name')] \ + if pkg.get('arch', None) == inst.get('arch', None)] + + if len(arch_match) > 1: + self.logger.error("Multiple instances of package %s installed with the same achitecture." % \ + (entry.get('name'))) + elif len(arch_match) == 1: + # There is only one installed like there should be. + # Check that it is the right version. + for pkg in arch_match: + if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) or \ + self.inst_evra_equal(inst, pkg): + self.logger.debug(" %s" % self.str_evra(inst)) + self.instance_status[inst]['installed'] = True + + if (self.pkg_verify and + inst.get('pkg_verify', 'true').lower() == 'true'): + flags = inst.get('verify_flags', '').split(',') + self.verify_flags + if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ + 'nosignature' not in flags: + flags += ['nosignature', 'nodigest'] + self.logger.info('WARNING: Package %s %s requires GPG Public key with ID %s'\ + % (pkg.get('name'), self.str_evra(pkg), \ + pkg.get('gpgkeyid', ''))) + self.logger.info(' Disabling signature check.') + + if self.setup.get('quick', False): + if rpmtools.prelink_exists: + flags += ['nomd5', 'nosize'] + else: + flags += ['nomd5'] + self.logger.debug(" verify_flags = %s" % flags) + + if inst.get('verify', 'true') == 'false': + self.instance_status[inst]['verify'] = None + else: + vp_ts = rpmtools.rpmtransactionset() + self.instance_status[inst]['verify'] = \ + rpmtools.rpm_verify( vp_ts, pkg, flags ) + vp_ts.closeDB() + del vp_ts + + else: + # Wrong version installed. + self.instance_status[inst]['version_fail'] = True + self.logger.info(" Wrong version installed. Want %s, but have %s"\ + % (self.str_evra(inst), self.str_evra(pkg))) + + qtext_versions = qtext_versions + 'U(%s -> %s) ' % \ + (self.str_evra(pkg), self.str_evra(inst)) + elif len(arch_match) == 0: + # This instance is not installed. + self.instance_status[inst]['installed'] = False + self.logger.info(" %s is not installed." % self.str_evra(inst)) + qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) + + # Check the rpm verify results. + for inst in instances: + instance_fail = False + # Dump the rpm verify results. + #****Write something to format this nicely.***** + if self.setup['debug'] and self.instance_status[inst].get('verify', None): + self.logger.debug(self.instance_status[inst]['verify']) + + self.instance_status[inst]['verify_fail'] = False + if self.instance_status[inst].get('verify', None): + if len(self.instance_status[inst].get('verify')) > 1: + self.logger.info("WARNING: Verification of more than one package instance.") + + for result in self.instance_status[inst]['verify']: + + # Check header results + if result.get('hdr', None): + instance_fail = True + self.instance_status[inst]['verify_fail'] = True + + # Check dependency results + if result.get('deps', None): + instance_fail = True + self.instance_status[inst]['verify_fail'] = True + + # Check the rpm verify file results against the modlist + # and entry and per Instance Ignores. + ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ + [ig.get('name') for ig in inst.findall('Ignore')] + \ + self.ignores + for file_result in result.get('files', []): + if file_result[-1] not in modlist + ignores: + instance_fail = True + self.instance_status[inst]['verify_fail'] = True + else: + self.logger.debug(" Modlist/Ignore match: %s" % \ + (file_result[-1])) + + if instance_fail == True: + self.logger.debug("*** Instance %s failed RPM verification ***" % \ + self.str_evra(inst)) + qtext_versions = qtext_versions + 'R(%s) ' % self.str_evra(inst) + self.modlists[entry] = modlist + + # Attach status structure for return to server for reporting. + inst.set('verify_status', str(self.instance_status[inst])) + + if self.instance_status[inst]['installed'] == False or \ + self.instance_status[inst].get('version_fail', False)== True or \ + self.instance_status[inst].get('verify_fail', False) == True: + package_fail = True + self.instance_status[inst]['pkg'] = entry + self.modlists[entry] = modlist + + # Find Installed Instances that are not in the Config. + extra_installed = self.FindExtraInstances(entry, self.installed[entry.get('name')]) + if extra_installed != None: + package_fail = True + self.extra_instances.append(extra_installed) + for inst in extra_installed.findall('Instance'): + qtext_versions = qtext_versions + 'D(%s) ' % self.str_evra(inst) + self.logger.debug("Found Extra Instances %s" % qtext_versions) + + if package_fail == True: + self.logger.info(" Package %s failed verification." % \ + (entry.get('name'))) + qtext = 'Install/Upgrade/delete Package %s instance(s) - %s (y/N) ' % \ + (entry.get('name'), qtext_versions) + entry.set('qtext', qtext) + + bcfg2_versions = '' + for bcfg2_inst in [inst for inst in instances if inst.tag == 'Instance']: + bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(bcfg2_inst) + if bcfg2_versions != '': + entry.set('version', bcfg2_versions) + installed_versions = '' + + for installed_inst in self.installed[entry.get('name')]: + installed_versions = installed_versions + '(%s) ' % \ + self.str_evra(installed_inst) + + entry.set('current_version', installed_versions) + return False + + else: + # There are no Instances of this package installed. + self.logger.debug("Package %s has no instances installed" % (entry.get('name'))) + entry.set('current_exists', 'false') + bcfg2_versions = '' + for inst in instances: + qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) + self.instance_status.setdefault(inst, {})['installed'] = False + self.modlists[entry] = modlist + self.instance_status[inst]['pkg'] = entry + if inst.tag == 'Instance': + bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(inst) + if bcfg2_versions != '': + entry.set('version', bcfg2_versions) + entry.set('qtext', "Install Package %s Instance(s) %s? (y/N) " % \ + (entry.get('name'), qtext_versions)) + + return False + return True + + def RemovePackages(self, packages): + """ + Remove specified entries. + + packages is a list of Package Entries with Instances generated + by FindExtraPackages(). + + """ + self.logger.debug('Running RPM.RemovePackages()') + + pkgspec_list = [] + for pkg in packages: + for inst in pkg: + if pkg.get('name') != 'gpg-pubkey': + pkgspec = { 'name':pkg.get('name'), + 'epoch':inst.get('epoch', None), + 'version':inst.get('version'), + 'release':inst.get('release'), + 'arch':inst.get('arch') } + pkgspec_list.append(pkgspec) + else: + pkgspec = { 'name':pkg.get('name'), + 'version':inst.get('version'), + 'release':inst.get('release')} + self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ + % (pkgspec.get('name'), self.str_evra(pkgspec))) + self.logger.info(" This package will be deleted in a future version of the RPM driver.") + #pkgspec_list.append(pkg_spec) + + erase_results = rpmtools.rpm_erase(pkgspec_list, self.erase_flags) + if erase_results == []: + self.modified += packages + for pkg in pkgspec_list: + self.logger.info("Deleted %s %s" % (pkg.get('name'), self.str_evra(pkg))) + else: + self.logger.info("Bulk erase failed with errors:") + self.logger.debug("Erase results = %s" % erase_results) + self.logger.info("Attempting individual erase for each package.") + pkgspec_list = [] + for pkg in packages: + pkg_modified = False + for inst in pkg: + if pkg.get('name') != 'gpg-pubkey': + pkgspec = { 'name':pkg.get('name'), + 'epoch':inst.get('epoch', None), + 'version':inst.get('version'), + 'release':inst.get('release'), + 'arch':inst.get('arch') } + pkgspec_list.append(pkgspec) + else: + pkgspec = { 'name':pkg.get('name'), + 'version':inst.get('version'), + 'release':inst.get('release')} + self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ + % (pkgspec.get('name'), self.str_evra(pkgspec))) + self.logger.info(" This package will be deleted in a future version of the RPM driver.") + continue # Don't delete the gpg-pubkey packages for now. + erase_results = rpmtools.rpm_erase([pkgspec], self.erase_flags) + if erase_results == []: + pkg_modified = True + self.logger.info("Deleted %s %s" % \ + (pkgspec.get('name'), self.str_evra(pkgspec))) + else: + self.logger.error("unable to delete %s %s" % \ + (pkgspec.get('name'), self.str_evra(pkgspec))) + self.logger.debug("Failure = %s" % erase_results) + if pkg_modified == True: + self.modified.append(pkg) + + self.RefreshPackages() + self.extra = self.FindExtraPackages() + + def FixInstance(self, instance, inst_status): + """ + Control if a reinstall of a package happens or not based on the + results from RPM.VerifyPackage(). + + Return True to reinstall, False to not reintstall. + + """ + fix = False + + if inst_status.get('installed', False) == False: + if instance.get('installed_action', 'install') == "install" and \ + self.installed_action == "install": + fix = True + else: + self.logger.debug('Installed Action for %s %s is to not install' % \ + (inst_status.get('pkg').get('name'), + self.str_evra(instance))) + + elif inst_status.get('version_fail', False) == True: + if instance.get('version_fail_action', 'upgrade') == "upgrade" and \ + self.version_fail_action == "upgrade": + fix = True + else: + self.logger.debug('Version Fail Action for %s %s is to not upgrade' % \ + (inst_status.get('pkg').get('name'), + self.str_evra(instance))) + + elif inst_status.get('verify_fail', False) == True and self.name == "RPM": + # yum can't reinstall packages so only do this for rpm. + if instance.get('verify_fail_action', 'reinstall') == "reinstall" and \ + self.verify_fail_action == "reinstall": + for inst in inst_status.get('verify'): + # This needs to be a for loop rather than a straight get() + # because the underlying routines handle multiple packages + # and return a list of results. + self.logger.debug('reinstall_check: %s %s:%s-%s.%s' % inst.get('nevra')) + + if inst.get("hdr", False): + fix = True + + elif inst.get('files', False): + # Parse rpm verify file results + for file_result in inst.get('files', []): + self.logger.debug('reinstall_check: file: %s' % file_result) + if file_result[-2] != 'c': + fix = True + break + + # Shouldn't really need this, but included for clarity. + elif inst.get("deps", False): + fix = False + else: + self.logger.debug('Verify Fail Action for %s %s is to not reinstall' % \ + (inst_status.get('pkg').get('name'), + self.str_evra(instance))) + + return fix + + def Install(self, packages, states): + """ + Try and fix everything that RPM.VerifyPackages() found wrong for + each Package Entry. This can result in individual RPMs being + installed (for the first time), reinstalled, deleted, downgraded + or upgraded. + + packages is a list of Package Elements that has + states[<Package Element>] == False + + The following effects occur: + - states{} is conditionally updated for each package. + - self.installed{} is rebuilt, possibly multiple times. + - self.instance_statusi{} is conditionally updated for each instance + of a package. + - Each package will be added to self.modified[] if its states{} + entry is set to True. + + """ + self.logger.info('Runing RPM.Install()') + + install_only_pkgs = [] + gpg_keys = [] + upgrade_pkgs = [] + + # Remove extra instances. + # Can not reverify because we don't have a package entry. + if len(self.extra_instances) > 0: + if (self.setup.get('remove') == 'all' or \ + self.setup.get('remove') == 'packages') and\ + not self.setup.get('dryrun'): + self.RemovePackages(self.extra_instances) + else: + self.logger.info("The following extra package instances will be removed by the '-r' option:") + for pkg in self.extra_instances: + for inst in pkg: + self.logger.info(" %s %s" % (pkg.get('name'), self.str_evra(inst))) + + # Figure out which instances of the packages actually need something + # doing to them and place in the appropriate work 'queue'. + for pkg in packages: + for inst in [instn for instn in pkg if instn.tag \ + in ['Instance', 'Package']]: + if self.FixInstance(inst, self.instance_status[inst]): + if pkg.get('name') == 'gpg-pubkey': + gpg_keys.append(inst) + elif pkg.get('name') in self.installOnlyPkgs: + install_only_pkgs.append(inst) + else: + upgrade_pkgs.append(inst) + + # Fix installOnlyPackages + if len(install_only_pkgs) > 0: + self.logger.info("Attempting to install 'install only packages'") + install_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) \ + for inst in install_only_pkgs]) + self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args) + cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \ + install_args) + if cmdrc == 0: + # The rpm command succeeded. All packages installed. + self.logger.info("Single Pass for InstallOnlyPkgs Succeded") + self.RefreshPackages() + + else: + # The rpm command failed. No packages installed. + # Try installing instances individually. + self.logger.error("Single Pass for InstallOnlyPackages Failed") + installed_instances = [] + for inst in install_only_pkgs: + install_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) + self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args) + cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \ + install_args) + if cmdrc == 0: + installed_instances.append(inst) + else: + self.logger.debug("InstallOnlyPackage %s %s would not install." % \ + (self.instance_status[inst].get('pkg').get('name'), \ + self.str_evra(inst))) + + install_pkg_set = set([self.instance_status[inst].get('pkg') \ + for inst in install_only_pkgs]) + self.RefreshPackages() + + # Install GPG keys. + if len(gpg_keys) > 0: + for inst in gpg_keys: + self.logger.info("Installing GPG keys.") + key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) + cmdrc, output = self.cmd.run("rpm --import %s" % key_arg) + if cmdrc != 0: + self.logger.debug("Unable to install %s-%s" % \ + (self.instance_status[inst].get('pkg').get('name'), \ + self.str_evra(inst))) + else: + self.logger.debug("Installed %s-%s-%s" % \ + (self.instance_status[inst].get('pkg').get('name'), \ + inst.get('version'), inst.get('release'))) + self.RefreshPackages() + self.gpg_keyids = self.getinstalledgpg() + pkg = self.instance_status[gpg_keys[0]].get('pkg') + states[pkg] = self.VerifyPackage(pkg, []) + + # Fix upgradeable packages. + if len(upgrade_pkgs) > 0: + self.logger.info("Attempting to upgrade packages") + upgrade_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) \ + for inst in upgrade_pkgs]) + cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \ + upgrade_args) + if cmdrc == 0: + # The rpm command succeeded. All packages upgraded. + self.logger.info("Single Pass for Upgraded Packages Succeded") + upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \ + for inst in upgrade_pkgs]) + self.RefreshPackages() + else: + # The rpm command failed. No packages upgraded. + # Try upgrading instances individually. + self.logger.error("Single Pass for Upgrading Packages Failed") + upgraded_instances = [] + for inst in upgrade_pkgs: + upgrade_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) + #self.logger.debug("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \ + # upgrade_args) + cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % upgrade_args) + if cmdrc == 0: + upgraded_instances.append(inst) + else: + self.logger.debug("Package %s %s would not upgrade." % \ + (self.instance_status[inst].get('pkg').get('name'), \ + self.str_evra(inst))) + + upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \ + for inst in upgrade_pkgs]) + self.RefreshPackages() + + if not self.setup['kevlar']: + for pkg_entry in packages: + self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name'))) + states[pkg_entry] = self.VerifyPackage(pkg_entry, \ + self.modlists.get(pkg_entry, [])) + + for entry in [ent for ent in packages if states[ent]]: + self.modified.append(entry) + + def canInstall(self, entry): + """Test if entry has enough information to be installed.""" + if not self.handlesEntry(entry): + return False + + if 'failure' in entry.attrib: + self.logger.error("Cannot install entry %s:%s with bind failure" % \ + (entry.tag, entry.get('name'))) + return False + + + instances = entry.findall('Instance') + + # If the entry wasn't verifiable, then we really don't want to try and fix something + # that we don't know is broken. + if not self.canVerify(entry): + self.logger.debug("WARNING: Package %s was not verifiable, not passing to Install()" \ + % entry.get('name')) + return False + + if not instances: + # Old non Instance format, unmodified. + if entry.get('name') == 'gpg-pubkey': + # gpg-pubkey packages aren't really pacakges, so we have to do + # something a little different. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__gpg_ireq__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot install" \ + % (entry.tag, entry.get('name'))) + return False + else: + if [attr for attr in self.__ireq__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot install" \ + % (entry.tag, entry.get('name'))) + return False + else: + if entry.get('name') == 'gpg-pubkey': + # gpg-pubkey packages aren't really pacakges, so we have to do + # something a little different. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__new_gpg_ireq__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot install" \ + % (entry.tag, entry.get('name'))) + return False + # Check that the Instance Level has what we need for verification. + for inst in instances: + if [attr for attr in self.__new_gpg_ireq__[inst.tag] \ + if attr not in inst.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot install"\ + % (inst.tag, entry.get('name'))) + return False + else: + # New format with Instances. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__new_ireq__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot install" \ + % (entry.tag, entry.get('name'))) + self.logger.error(" Required attributes that may not be present are %s" \ + % (self.__new_ireq__[entry.tag])) + return False + # Check that the Instance Level has what we need for verification. + for inst in instances: + if inst.tag == 'Instance': + if [attr for attr in self.__new_ireq__[inst.tag] \ + if attr not in inst.attrib]: + self.logger.error("Incomplete information for %s of package %s; cannot install" \ + % (inst.tag, entry.get('name'))) + self.logger.error(" Required attributes that may not be present are %s" \ + % (self.__new_ireq__[inst.tag])) + return False + return True + + def canVerify(self, entry): + """ + Test if entry has enough information to be verified. + + Three types of entries are checked. + Old style Package + New style Package with Instances + pgp-pubkey packages + + Also the old style entries get modified after the first + VerifyPackage() run, so there needs to be a second test. + + """ + if not self.handlesEntry(entry): + return False + + if 'failure' in entry.attrib: + self.logger.error("Entry %s:%s reports bind failure: %s" % \ + (entry.tag, entry.get('name'), entry.get('failure'))) + return False + + # We don't want to do any checks so we don't care what the entry has in it. + if (not self.pkg_checks or + entry.get('pkg_checks', 'true').lower() == 'false'): + return True + + instances = entry.findall('Instance') + + if not instances: + # Old non Instance format, unmodified. + if entry.get('name') == 'gpg-pubkey': + # gpg-pubkey packages aren't really pacakges, so we have to do + # something a little different. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__gpg_req__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (entry.tag, entry.get('name'))) + return False + elif entry.tag == 'Path' and entry.get('type') == 'ignore': + # ignored Paths are only relevant during failed package + # verification + pass + else: + if [attr for attr in self.__req__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (entry.tag, entry.get('name'))) + return False + else: + if entry.get('name') == 'gpg-pubkey': + # gpg-pubkey packages aren't really pacakges, so we have to do + # something a little different. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__new_gpg_req__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (entry.tag, entry.get('name'))) + return False + # Check that the Instance Level has what we need for verification. + for inst in instances: + if [attr for attr in self.__new_gpg_req__[inst.tag] \ + if attr not in inst.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (inst.tag, inst.get('name'))) + return False + else: + # New format with Instances, or old style modified. + # Check that the Package Level has what we need for verification. + if [attr for attr in self.__new_req__[entry.tag] if attr not in entry.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (entry.tag, entry.get('name'))) + return False + # Check that the Instance Level has what we need for verification. + for inst in instances: + if inst.tag == 'Instance': + if [attr for attr in self.__new_req__[inst.tag] \ + if attr not in inst.attrib]: + self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ + % (inst.tag, inst.get('name'))) + return False + return True + + def FindExtraPackages(self): + """Find extra packages.""" + packages = [entry.get('name') for entry in self.getSupportedEntries()] + extras = [] + + for (name, instances) in list(self.installed.items()): + if name not in packages: + extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) + for installed_inst in instances: + if self.setup['extra']: + self.logger.info("Extra Package %s %s." % \ + (name, self.str_evra(installed_inst))) + tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ + version = installed_inst.get('version'), \ + release = installed_inst.get('release')) + if installed_inst.get('epoch', None) != None: + tmp_entry.set('epoch', str(installed_inst.get('epoch'))) + if installed_inst.get('arch', None) != None: + tmp_entry.set('arch', installed_inst.get('arch')) + extras.append(extra_entry) + return extras + + + def FindExtraInstances(self, pkg_entry, installed_entry): + """ + Check for installed instances that are not in the config. + Return a Package Entry with Instances to remove, or None if there + are no Instances to remove. + + """ + name = pkg_entry.get('name') + extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) + instances = [inst for inst in pkg_entry if inst.tag == 'Instance' or inst.tag == 'Package'] + if name in self.installOnlyPkgs: + for installed_inst in installed_entry: + not_found = True + for inst in instances: + if self.pkg_vr_equal(inst, installed_inst) or \ + self.inst_evra_equal(inst, installed_inst): + not_found = False + break + if not_found == True: + # Extra package. + self.logger.info("Extra InstallOnlyPackage %s %s." % \ + (name, self.str_evra(installed_inst))) + tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ + version = installed_inst.get('version'), \ + release = installed_inst.get('release')) + if installed_inst.get('epoch', None) != None: + tmp_entry.set('epoch', str(installed_inst.get('epoch'))) + if installed_inst.get('arch', None) != None: + tmp_entry.set('arch', installed_inst.get('arch')) + else: + # Normal package, only check arch. + for installed_inst in installed_entry: + not_found = True + for inst in instances: + if installed_inst.get('arch', None) == inst.get('arch', None) or\ + inst.tag == 'Package': + not_found = False + break + if not_found: + self.logger.info("Extra Normal Package Instance %s %s" % \ + (name, self.str_evra(installed_inst))) + tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ + version = installed_inst.get('version'), \ + release = installed_inst.get('release')) + if installed_inst.get('epoch', None) != None: + tmp_entry.set('epoch', str(installed_inst.get('epoch'))) + if installed_inst.get('arch', None) != None: + tmp_entry.set('arch', installed_inst.get('arch')) + + if len(extra_entry) == 0: + extra_entry = None + + return extra_entry + + def str_evra(self, instance): + """Convert evra dict entries to a string.""" + if instance.get('epoch', '*') in ['*', None]: + return '%s-%s.%s' % (instance.get('version', '*'), + instance.get('release', '*'), + instance.get('arch', '*')) + else: + return '%s:%s-%s.%s' % (instance.get('epoch', '*'), + instance.get('version', '*'), + instance.get('release', '*'), + instance.get('arch', '*')) + + def pkg_vr_equal(self, config_entry, installed_entry): + ''' + Compare old style entry to installed entry. Which means ignore + the epoch and arch. + ''' + if (config_entry.tag == 'Package' and \ + config_entry.get('version') == installed_entry.get('version') and \ + config_entry.get('release') == installed_entry.get('release')): + return True + else: + return False + + def inst_evra_equal(self, config_entry, installed_entry): + """Compare new style instance to installed entry.""" + + if config_entry.get('epoch', None) != None: + epoch = int(config_entry.get('epoch')) + else: + epoch = None + + if (config_entry.tag == 'Instance' and \ + (epoch == installed_entry.get('epoch', 0) or \ + (epoch == 0 and installed_entry.get('epoch', 0) == None) or \ + (epoch == None and installed_entry.get('epoch', 0) == 0)) and \ + config_entry.get('version') == installed_entry.get('version') and \ + config_entry.get('release') == installed_entry.get('release') and \ + config_entry.get('arch', None) == installed_entry.get('arch', None)): + return True + else: + return False + + def getinstalledgpg(self): + """ + Create a list of installed GPG key IDs. + + The pgp-pubkey package version is the least significant 4 bytes + (big-endian) of the key ID which is good enough for our purposes. + + """ + init_ts = rpmtools.rpmtransactionset() + init_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) + gpg_hdrs = rpmtools.getheadersbykeyword(init_ts, **{'name':'gpg-pubkey'}) + keyids = [ header[rpm.RPMTAG_VERSION] for header in gpg_hdrs] + keyids.append('None') + init_ts.closeDB() + del init_ts + return keyids + + def VerifyPath(self, entry, _): + """ + We don't do anything here since all + Paths are processed in __init__ + """ + return True diff --git a/src/lib/Bcfg2/Client/Tools/RPMng.py b/src/lib/Bcfg2/Client/Tools/RPMng.py index 91e2180ae..f5a234f8d 100644 --- a/src/lib/Bcfg2/Client/Tools/RPMng.py +++ b/src/lib/Bcfg2/Client/Tools/RPMng.py @@ -1,987 +1,8 @@ -"""Bcfg2 Support for RPMS""" +""" RPM driver called 'RPMng' for backwards compat """ -import os.path -import rpm -import rpmtools -import Bcfg2.Client.Tools +from Bcfg2.Client.Tools.RPM import RPM -class RPMng(Bcfg2.Client.Tools.PkgTool): - """Support for RPM packages.""" - name = 'RPMng' - __execs__ = ['/bin/rpm', '/var/lib/rpm'] - __handles__ = [('Package', 'rpm')] - - __req__ = {'Package': ['name', 'version']} - __ireq__ = {'Package': ['url']} - - __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']} - __new_ireq__ = {'Package': ['uri'], \ - 'Instance': ['simplefile']} - - __gpg_req__ = {'Package': ['name', 'version']} - __gpg_ireq__ = {'Package': ['name', 'version']} - - __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']} - __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']} - - conflicts = ['RPM'] - - pkgtype = 'rpm' - pkgtool = ("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"])) - - def __init__(self, logger, setup, config): - Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) - - # create a global ignore list used when ignoring particular - # files during package verification - self.ignores = [entry.get('name') for struct in config for entry in struct \ - if entry.get('type') == 'ignore'] - self.instance_status = {} - self.extra_instances = [] - self.modlists = {} - self.gpg_keyids = self.getinstalledgpg() - - opt_prefix = self.name.lower() - self.installOnlyPkgs = self.setup["%s_installonly" % opt_prefix] - if 'gpg-pubkey' not in self.installOnlyPkgs: - self.installOnlyPkgs.append('gpg-pubkey') - self.erase_flags = self.setup['%s_erase_flags' % opt_prefix] - self.pkg_checks = self.setup['%s_pkg_checks' % opt_prefix] - self.pkg_verify = self.setup['%s_pkg_verify' % opt_prefix] - self.installed_action = self.setup['%s_installed_action' % opt_prefix] - self.version_fail_action = self.setup['%s_version_fail_action' % - opt_prefix] - self.verify_fail_action = self.setup['%s_verify_fail_action' % - opt_prefix] - self.verify_flags = self.setup['%s_verify_flags' % opt_prefix] - if '' in self.verify_flags: - self.verify_flags.remove('') - - self.logger.debug('%s: installOnlyPackages = %s' % - (self.name, self.installOnlyPkgs)) - self.logger.debug('%s: erase_flags = %s' % - (self.name, self.erase_flags)) - self.logger.debug('%s: pkg_checks = %s' % - (self.name, self.pkg_checks)) - self.logger.debug('%s: pkg_verify = %s' % - (self.name, self.pkg_verify)) - self.logger.debug('%s: installed_action = %s' % - (self.name, self.installed_action)) - self.logger.debug('%s: version_fail_action = %s' % - (self.name, self.version_fail_action)) - self.logger.debug('%s: verify_fail_action = %s' % - (self.name, self.verify_fail_action)) - self.logger.debug('%s: verify_flags = %s' % - (self.name, self.verify_flags)) - - # Force a re- prelink of all packages if prelink exists. - # Many, if not most package verifies can be caused by out of - # date prelinking. - if os.path.isfile('/usr/sbin/prelink') and not self.setup['dryrun']: - cmdrc, output = self.cmd.run('/usr/sbin/prelink -a -mR') - if cmdrc == 0: - self.logger.debug('Pre-emptive prelink succeeded') - else: - # FIXME : this is dumb - what if the output is huge? - self.logger.error('Pre-emptive prelink failed: %s' % output) - - - def RefreshPackages(self): - """ - Creates self.installed{} which is a dict of installed packages. - - The dict items are lists of nevra dicts. This loosely matches the - config from the server and what rpmtools uses to specify pacakges. - - e.g. - - self.installed['foo'] = [ {'name':'foo', 'epoch':None, - 'version':'1', 'release':2, - 'arch':'i386'}, - {'name':'foo', 'epoch':None, - 'version':'1', 'release':2, - 'arch':'x86_64'} ] - """ - self.installed = {} - refresh_ts = rpmtools.rpmtransactionset() - # Don't bother with signature checks at this stage. The GPG keys might - # not be installed. - refresh_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) - for nevra in rpmtools.rpmpackagelist(refresh_ts): - self.installed.setdefault(nevra['name'], []).append(nevra) - if self.setup['debug']: - print("The following package instances are installed:") - for name, instances in list(self.installed.items()): - self.logger.debug(" " + name) - for inst in instances: - self.logger.debug(" %s" %self.str_evra(inst)) - refresh_ts.closeDB() - del refresh_ts - - def VerifyPackage(self, entry, modlist, pinned_version=None): - """ - Verify Package status for entry. - Performs the following: - - Checks for the presence of required Package Instances. - - Compares the evra 'version' info against self.installed{}. - - RPM level package verify (rpm --verify). - - Checks for the presence of unrequired package instances. - - Produces the following dict and list for RPMng.Install() to use: - For installs/upgrades/fixes of required instances: - instance_status = { <Instance Element Object>: - { 'installed': True|False, - 'version_fail': True|False, - 'verify_fail': True|False, - 'pkg': <Package Element Object>, - 'modlist': [ <filename>, ... ], - 'verify' : [ <rpm --verify results> ] - }, ...... - } - - For deletions of unrequired instances: - extra_instances = [ <Package Element Object>, ..... ] - - Constructs the text prompts for interactive mode. - """ - instances = [inst for inst in entry if inst.tag == 'Instance' or inst.tag == 'Package'] - if instances == []: - # We have an old style no Instance entry. Convert it to new style. - instance = Bcfg2.Client.XML.SubElement(entry, 'Package') - for attrib in list(entry.attrib.keys()): - instance.attrib[attrib] = entry.attrib[attrib] - if (self.pkg_checks and - entry.get('pkg_checks', 'true').lower() == 'true'): - if 'any' in [entry.get('version'), pinned_version]: - version, release = 'any', 'any' - elif entry.get('version') == 'auto': - if pinned_version != None: - version, release = pinned_version.split('-') - else: - return False - else: - version, release = entry.get('version').split('-') - instance.set('version', version) - instance.set('release', release) - if entry.get('verify', 'true') == 'false': - instance.set('verify', 'false') - instances = [ instance ] - - self.logger.debug("Verifying package instances for %s" % entry.get('name')) - package_fail = False - qtext_versions = '' - - if entry.get('name') in self.installed: - # There is at least one instance installed. - if (self.pkg_checks and - entry.get('pkg_checks', 'true').lower() == 'true'): - rpmTs = rpm.TransactionSet() - rpmHeader = None - for h in rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')): - if rpmHeader is None or rpm.versionCompare(h, rpmHeader) > 0: - rpmHeader = h - rpmProvides = [ h['provides'] for h in \ - rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')) ] - rpmIntersection = set(rpmHeader['provides']) & \ - set(self.installOnlyPkgs) - if len(rpmIntersection) > 0: - # Packages that should only be installed or removed. - # e.g. kernels. - self.logger.debug(" Install only package.") - for inst in instances: - self.instance_status.setdefault(inst, {})['installed'] = False - self.instance_status[inst]['version_fail'] = False - if inst.tag == 'Package' and len(self.installed[entry.get('name')]) > 1: - self.logger.error("WARNING: Multiple instances of package %s are installed." % \ - (entry.get('name'))) - for pkg in self.installed[entry.get('name')]: - if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) \ - or self.inst_evra_equal(inst, pkg): - if inst.get('version') == 'any': - self.logger.error("got any version") - self.logger.debug(" %s" % self.str_evra(inst)) - self.instance_status[inst]['installed'] = True - - if (self.pkg_verify and - inst.get('pkg_verify', 'true').lower() == 'true'): - flags = inst.get('verify_flags', '').split(',') + self.verify_flags - if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ - entry.get('name') != 'gpg-pubkey': - flags += ['nosignature', 'nodigest'] - self.logger.debug('WARNING: Package %s %s requires GPG Public key with ID %s'\ - % (pkg.get('name'), self.str_evra(pkg), \ - pkg.get('gpgkeyid', ''))) - self.logger.debug(' Disabling signature check.') - - if self.setup.get('quick', False): - if rpmtools.prelink_exists: - flags += ['nomd5', 'nosize'] - else: - flags += ['nomd5'] - self.logger.debug(" verify_flags = %s" % flags) - - if inst.get('verify', 'true') == 'false': - self.instance_status[inst]['verify'] = None - else: - vp_ts = rpmtools.rpmtransactionset() - self.instance_status[inst]['verify'] = \ - rpmtools.rpm_verify( vp_ts, pkg, flags) - vp_ts.closeDB() - del vp_ts - - if self.instance_status[inst]['installed'] == False: - self.logger.info(" Package %s %s not installed." % \ - (entry.get('name'), self.str_evra(inst))) - - qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) - entry.set('current_exists', 'false') - else: - # Normal Packages that can be upgraded. - for inst in instances: - self.instance_status.setdefault(inst, {})['installed'] = False - self.instance_status[inst]['version_fail'] = False - - # Only installed packages with the same architecture are - # relevant. - if inst.get('arch', None) == None: - arch_match = self.installed[entry.get('name')] - else: - arch_match = [pkg for pkg in self.installed[entry.get('name')] \ - if pkg.get('arch', None) == inst.get('arch', None)] - - if len(arch_match) > 1: - self.logger.error("Multiple instances of package %s installed with the same achitecture." % \ - (entry.get('name'))) - elif len(arch_match) == 1: - # There is only one installed like there should be. - # Check that it is the right version. - for pkg in arch_match: - if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) or \ - self.inst_evra_equal(inst, pkg): - self.logger.debug(" %s" % self.str_evra(inst)) - self.instance_status[inst]['installed'] = True - - if (self.pkg_verify and - inst.get('pkg_verify', 'true').lower() == 'true'): - flags = inst.get('verify_flags', '').split(',') + self.verify_flags - if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ - 'nosignature' not in flags: - flags += ['nosignature', 'nodigest'] - self.logger.info('WARNING: Package %s %s requires GPG Public key with ID %s'\ - % (pkg.get('name'), self.str_evra(pkg), \ - pkg.get('gpgkeyid', ''))) - self.logger.info(' Disabling signature check.') - - if self.setup.get('quick', False): - if rpmtools.prelink_exists: - flags += ['nomd5', 'nosize'] - else: - flags += ['nomd5'] - self.logger.debug(" verify_flags = %s" % flags) - - if inst.get('verify', 'true') == 'false': - self.instance_status[inst]['verify'] = None - else: - vp_ts = rpmtools.rpmtransactionset() - self.instance_status[inst]['verify'] = \ - rpmtools.rpm_verify( vp_ts, pkg, flags ) - vp_ts.closeDB() - del vp_ts - - else: - # Wrong version installed. - self.instance_status[inst]['version_fail'] = True - self.logger.info(" Wrong version installed. Want %s, but have %s"\ - % (self.str_evra(inst), self.str_evra(pkg))) - - qtext_versions = qtext_versions + 'U(%s -> %s) ' % \ - (self.str_evra(pkg), self.str_evra(inst)) - elif len(arch_match) == 0: - # This instance is not installed. - self.instance_status[inst]['installed'] = False - self.logger.info(" %s is not installed." % self.str_evra(inst)) - qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) - - # Check the rpm verify results. - for inst in instances: - instance_fail = False - # Dump the rpm verify results. - #****Write something to format this nicely.***** - if self.setup['debug'] and self.instance_status[inst].get('verify', None): - self.logger.debug(self.instance_status[inst]['verify']) - - self.instance_status[inst]['verify_fail'] = False - if self.instance_status[inst].get('verify', None): - if len(self.instance_status[inst].get('verify')) > 1: - self.logger.info("WARNING: Verification of more than one package instance.") - - for result in self.instance_status[inst]['verify']: - - # Check header results - if result.get('hdr', None): - instance_fail = True - self.instance_status[inst]['verify_fail'] = True - - # Check dependency results - if result.get('deps', None): - instance_fail = True - self.instance_status[inst]['verify_fail'] = True - - # Check the rpm verify file results against the modlist - # and entry and per Instance Ignores. - ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ - [ig.get('name') for ig in inst.findall('Ignore')] + \ - self.ignores - for file_result in result.get('files', []): - if file_result[-1] not in modlist + ignores: - instance_fail = True - self.instance_status[inst]['verify_fail'] = True - else: - self.logger.debug(" Modlist/Ignore match: %s" % \ - (file_result[-1])) - - if instance_fail == True: - self.logger.debug("*** Instance %s failed RPM verification ***" % \ - self.str_evra(inst)) - qtext_versions = qtext_versions + 'R(%s) ' % self.str_evra(inst) - self.modlists[entry] = modlist - - # Attach status structure for return to server for reporting. - inst.set('verify_status', str(self.instance_status[inst])) - - if self.instance_status[inst]['installed'] == False or \ - self.instance_status[inst].get('version_fail', False)== True or \ - self.instance_status[inst].get('verify_fail', False) == True: - package_fail = True - self.instance_status[inst]['pkg'] = entry - self.modlists[entry] = modlist - - # Find Installed Instances that are not in the Config. - extra_installed = self.FindExtraInstances(entry, self.installed[entry.get('name')]) - if extra_installed != None: - package_fail = True - self.extra_instances.append(extra_installed) - for inst in extra_installed.findall('Instance'): - qtext_versions = qtext_versions + 'D(%s) ' % self.str_evra(inst) - self.logger.debug("Found Extra Instances %s" % qtext_versions) - - if package_fail == True: - self.logger.info(" Package %s failed verification." % \ - (entry.get('name'))) - qtext = 'Install/Upgrade/delete Package %s instance(s) - %s (y/N) ' % \ - (entry.get('name'), qtext_versions) - entry.set('qtext', qtext) - - bcfg2_versions = '' - for bcfg2_inst in [inst for inst in instances if inst.tag == 'Instance']: - bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(bcfg2_inst) - if bcfg2_versions != '': - entry.set('version', bcfg2_versions) - installed_versions = '' - - for installed_inst in self.installed[entry.get('name')]: - installed_versions = installed_versions + '(%s) ' % \ - self.str_evra(installed_inst) - - entry.set('current_version', installed_versions) - return False - - else: - # There are no Instances of this package installed. - self.logger.debug("Package %s has no instances installed" % (entry.get('name'))) - entry.set('current_exists', 'false') - bcfg2_versions = '' - for inst in instances: - qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) - self.instance_status.setdefault(inst, {})['installed'] = False - self.modlists[entry] = modlist - self.instance_status[inst]['pkg'] = entry - if inst.tag == 'Instance': - bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(inst) - if bcfg2_versions != '': - entry.set('version', bcfg2_versions) - entry.set('qtext', "Install Package %s Instance(s) %s? (y/N) " % \ - (entry.get('name'), qtext_versions)) - - return False - return True - - def RemovePackages(self, packages): - """ - Remove specified entries. - - packages is a list of Package Entries with Instances generated - by FindExtraPackages(). - - """ - self.logger.debug('Running RPMng.RemovePackages()') - - pkgspec_list = [] - for pkg in packages: - for inst in pkg: - if pkg.get('name') != 'gpg-pubkey': - pkgspec = { 'name':pkg.get('name'), - 'epoch':inst.get('epoch', None), - 'version':inst.get('version'), - 'release':inst.get('release'), - 'arch':inst.get('arch') } - pkgspec_list.append(pkgspec) - else: - pkgspec = { 'name':pkg.get('name'), - 'version':inst.get('version'), - 'release':inst.get('release')} - self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ - % (pkgspec.get('name'), self.str_evra(pkgspec))) - self.logger.info(" This package will be deleted in a future version of the RPMng driver.") - #pkgspec_list.append(pkg_spec) - - erase_results = rpmtools.rpm_erase(pkgspec_list, self.erase_flags) - if erase_results == []: - self.modified += packages - for pkg in pkgspec_list: - self.logger.info("Deleted %s %s" % (pkg.get('name'), self.str_evra(pkg))) - else: - self.logger.info("Bulk erase failed with errors:") - self.logger.debug("Erase results = %s" % erase_results) - self.logger.info("Attempting individual erase for each package.") - pkgspec_list = [] - for pkg in packages: - pkg_modified = False - for inst in pkg: - if pkg.get('name') != 'gpg-pubkey': - pkgspec = { 'name':pkg.get('name'), - 'epoch':inst.get('epoch', None), - 'version':inst.get('version'), - 'release':inst.get('release'), - 'arch':inst.get('arch') } - pkgspec_list.append(pkgspec) - else: - pkgspec = { 'name':pkg.get('name'), - 'version':inst.get('version'), - 'release':inst.get('release')} - self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ - % (pkgspec.get('name'), self.str_evra(pkgspec))) - self.logger.info(" This package will be deleted in a future version of the RPMng driver.") - continue # Don't delete the gpg-pubkey packages for now. - erase_results = rpmtools.rpm_erase([pkgspec], self.erase_flags) - if erase_results == []: - pkg_modified = True - self.logger.info("Deleted %s %s" % \ - (pkgspec.get('name'), self.str_evra(pkgspec))) - else: - self.logger.error("unable to delete %s %s" % \ - (pkgspec.get('name'), self.str_evra(pkgspec))) - self.logger.debug("Failure = %s" % erase_results) - if pkg_modified == True: - self.modified.append(pkg) - - self.RefreshPackages() - self.extra = self.FindExtraPackages() - - def FixInstance(self, instance, inst_status): - """ - Control if a reinstall of a package happens or not based on the - results from RPMng.VerifyPackage(). - - Return True to reinstall, False to not reintstall. - - """ - fix = False - - if inst_status.get('installed', False) == False: - if instance.get('installed_action', 'install') == "install" and \ - self.installed_action == "install": - fix = True - else: - self.logger.debug('Installed Action for %s %s is to not install' % \ - (inst_status.get('pkg').get('name'), - self.str_evra(instance))) - - elif inst_status.get('version_fail', False) == True: - if instance.get('version_fail_action', 'upgrade') == "upgrade" and \ - self.version_fail_action == "upgrade": - fix = True - else: - self.logger.debug('Version Fail Action for %s %s is to not upgrade' % \ - (inst_status.get('pkg').get('name'), - self.str_evra(instance))) - - elif inst_status.get('verify_fail', False) == True and self.name == "RPMng": - # yum can't reinstall packages so only do this for rpm. - if instance.get('verify_fail_action', 'reinstall') == "reinstall" and \ - self.verify_fail_action == "reinstall": - for inst in inst_status.get('verify'): - # This needs to be a for loop rather than a straight get() - # because the underlying routines handle multiple packages - # and return a list of results. - self.logger.debug('reinstall_check: %s %s:%s-%s.%s' % inst.get('nevra')) - - if inst.get("hdr", False): - fix = True - - elif inst.get('files', False): - # Parse rpm verify file results - for file_result in inst.get('files', []): - self.logger.debug('reinstall_check: file: %s' % file_result) - if file_result[-2] != 'c': - fix = True - break - - # Shouldn't really need this, but included for clarity. - elif inst.get("deps", False): - fix = False - else: - self.logger.debug('Verify Fail Action for %s %s is to not reinstall' % \ - (inst_status.get('pkg').get('name'), - self.str_evra(instance))) - - return fix - - def Install(self, packages, states): - """ - Try and fix everything that RPMng.VerifyPackages() found wrong for - each Package Entry. This can result in individual RPMs being - installed (for the first time), reinstalled, deleted, downgraded - or upgraded. - - packages is a list of Package Elements that has - states[<Package Element>] == False - - The following effects occur: - - states{} is conditionally updated for each package. - - self.installed{} is rebuilt, possibly multiple times. - - self.instance_statusi{} is conditionally updated for each instance - of a package. - - Each package will be added to self.modified[] if its states{} - entry is set to True. - - """ - self.logger.info('Runing RPMng.Install()') - - install_only_pkgs = [] - gpg_keys = [] - upgrade_pkgs = [] - - # Remove extra instances. - # Can not reverify because we don't have a package entry. - if len(self.extra_instances) > 0: - if (self.setup.get('remove') == 'all' or \ - self.setup.get('remove') == 'packages') and\ - not self.setup.get('dryrun'): - self.RemovePackages(self.extra_instances) - else: - self.logger.info("The following extra package instances will be removed by the '-r' option:") - for pkg in self.extra_instances: - for inst in pkg: - self.logger.info(" %s %s" % (pkg.get('name'), self.str_evra(inst))) - - # Figure out which instances of the packages actually need something - # doing to them and place in the appropriate work 'queue'. - for pkg in packages: - for inst in [instn for instn in pkg if instn.tag \ - in ['Instance', 'Package']]: - if self.FixInstance(inst, self.instance_status[inst]): - if pkg.get('name') == 'gpg-pubkey': - gpg_keys.append(inst) - elif pkg.get('name') in self.installOnlyPkgs: - install_only_pkgs.append(inst) - else: - upgrade_pkgs.append(inst) - - # Fix installOnlyPackages - if len(install_only_pkgs) > 0: - self.logger.info("Attempting to install 'install only packages'") - install_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) \ - for inst in install_only_pkgs]) - self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args) - cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \ - install_args) - if cmdrc == 0: - # The rpm command succeeded. All packages installed. - self.logger.info("Single Pass for InstallOnlyPkgs Succeded") - self.RefreshPackages() - - else: - # The rpm command failed. No packages installed. - # Try installing instances individually. - self.logger.error("Single Pass for InstallOnlyPackages Failed") - installed_instances = [] - for inst in install_only_pkgs: - install_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) - self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args) - cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \ - install_args) - if cmdrc == 0: - installed_instances.append(inst) - else: - self.logger.debug("InstallOnlyPackage %s %s would not install." % \ - (self.instance_status[inst].get('pkg').get('name'), \ - self.str_evra(inst))) - - install_pkg_set = set([self.instance_status[inst].get('pkg') \ - for inst in install_only_pkgs]) - self.RefreshPackages() - - # Install GPG keys. - if len(gpg_keys) > 0: - for inst in gpg_keys: - self.logger.info("Installing GPG keys.") - key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) - cmdrc, output = self.cmd.run("rpm --import %s" % key_arg) - if cmdrc != 0: - self.logger.debug("Unable to install %s-%s" % \ - (self.instance_status[inst].get('pkg').get('name'), \ - self.str_evra(inst))) - else: - self.logger.debug("Installed %s-%s-%s" % \ - (self.instance_status[inst].get('pkg').get('name'), \ - inst.get('version'), inst.get('release'))) - self.RefreshPackages() - self.gpg_keyids = self.getinstalledgpg() - pkg = self.instance_status[gpg_keys[0]].get('pkg') - states[pkg] = self.VerifyPackage(pkg, []) - - # Fix upgradeable packages. - if len(upgrade_pkgs) > 0: - self.logger.info("Attempting to upgrade packages") - upgrade_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) \ - for inst in upgrade_pkgs]) - cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \ - upgrade_args) - if cmdrc == 0: - # The rpm command succeeded. All packages upgraded. - self.logger.info("Single Pass for Upgraded Packages Succeded") - upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \ - for inst in upgrade_pkgs]) - self.RefreshPackages() - else: - # The rpm command failed. No packages upgraded. - # Try upgrading instances individually. - self.logger.error("Single Pass for Upgrading Packages Failed") - upgraded_instances = [] - for inst in upgrade_pkgs: - upgrade_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) - #self.logger.debug("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \ - # upgrade_args) - cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % upgrade_args) - if cmdrc == 0: - upgraded_instances.append(inst) - else: - self.logger.debug("Package %s %s would not upgrade." % \ - (self.instance_status[inst].get('pkg').get('name'), \ - self.str_evra(inst))) - - upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \ - for inst in upgrade_pkgs]) - self.RefreshPackages() - - if not self.setup['kevlar']: - for pkg_entry in packages: - self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name'))) - states[pkg_entry] = self.VerifyPackage(pkg_entry, \ - self.modlists.get(pkg_entry, [])) - - for entry in [ent for ent in packages if states[ent]]: - self.modified.append(entry) - - def canInstall(self, entry): - """Test if entry has enough information to be installed.""" - if not self.handlesEntry(entry): - return False - - if 'failure' in entry.attrib: - self.logger.error("Cannot install entry %s:%s with bind failure" % \ - (entry.tag, entry.get('name'))) - return False - - - instances = entry.findall('Instance') - - # If the entry wasn't verifiable, then we really don't want to try and fix something - # that we don't know is broken. - if not self.canVerify(entry): - self.logger.debug("WARNING: Package %s was not verifiable, not passing to Install()" \ - % entry.get('name')) - return False - - if not instances: - # Old non Instance format, unmodified. - if entry.get('name') == 'gpg-pubkey': - # gpg-pubkey packages aren't really pacakges, so we have to do - # something a little different. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__gpg_ireq__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot install" \ - % (entry.tag, entry.get('name'))) - return False - else: - if [attr for attr in self.__ireq__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot install" \ - % (entry.tag, entry.get('name'))) - return False - else: - if entry.get('name') == 'gpg-pubkey': - # gpg-pubkey packages aren't really pacakges, so we have to do - # something a little different. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__new_gpg_ireq__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot install" \ - % (entry.tag, entry.get('name'))) - return False - # Check that the Instance Level has what we need for verification. - for inst in instances: - if [attr for attr in self.__new_gpg_ireq__[inst.tag] \ - if attr not in inst.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot install"\ - % (inst.tag, entry.get('name'))) - return False - else: - # New format with Instances. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__new_ireq__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot install" \ - % (entry.tag, entry.get('name'))) - self.logger.error(" Required attributes that may not be present are %s" \ - % (self.__new_ireq__[entry.tag])) - return False - # Check that the Instance Level has what we need for verification. - for inst in instances: - if inst.tag == 'Instance': - if [attr for attr in self.__new_ireq__[inst.tag] \ - if attr not in inst.attrib]: - self.logger.error("Incomplete information for %s of package %s; cannot install" \ - % (inst.tag, entry.get('name'))) - self.logger.error(" Required attributes that may not be present are %s" \ - % (self.__new_ireq__[inst.tag])) - return False - return True - - def canVerify(self, entry): - """ - Test if entry has enough information to be verified. - - Three types of entries are checked. - Old style Package - New style Package with Instances - pgp-pubkey packages - - Also the old style entries get modified after the first - VerifyPackage() run, so there needs to be a second test. - - """ - if not self.handlesEntry(entry): - return False - - if 'failure' in entry.attrib: - self.logger.error("Entry %s:%s reports bind failure: %s" % \ - (entry.tag, entry.get('name'), entry.get('failure'))) - return False - - # We don't want to do any checks so we don't care what the entry has in it. - if (not self.pkg_checks or - entry.get('pkg_checks', 'true').lower() == 'false'): - return True - - instances = entry.findall('Instance') - - if not instances: - # Old non Instance format, unmodified. - if entry.get('name') == 'gpg-pubkey': - # gpg-pubkey packages aren't really pacakges, so we have to do - # something a little different. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__gpg_req__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (entry.tag, entry.get('name'))) - return False - elif entry.tag == 'Path' and entry.get('type') == 'ignore': - # ignored Paths are only relevant during failed package - # verification - pass - else: - if [attr for attr in self.__req__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (entry.tag, entry.get('name'))) - return False - else: - if entry.get('name') == 'gpg-pubkey': - # gpg-pubkey packages aren't really pacakges, so we have to do - # something a little different. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__new_gpg_req__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (entry.tag, entry.get('name'))) - return False - # Check that the Instance Level has what we need for verification. - for inst in instances: - if [attr for attr in self.__new_gpg_req__[inst.tag] \ - if attr not in inst.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (inst.tag, inst.get('name'))) - return False - else: - # New format with Instances, or old style modified. - # Check that the Package Level has what we need for verification. - if [attr for attr in self.__new_req__[entry.tag] if attr not in entry.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (entry.tag, entry.get('name'))) - return False - # Check that the Instance Level has what we need for verification. - for inst in instances: - if inst.tag == 'Instance': - if [attr for attr in self.__new_req__[inst.tag] \ - if attr not in inst.attrib]: - self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ - % (inst.tag, inst.get('name'))) - return False - return True - - def FindExtraPackages(self): - """Find extra packages.""" - packages = [entry.get('name') for entry in self.getSupportedEntries()] - extras = [] - - for (name, instances) in list(self.installed.items()): - if name not in packages: - extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) - for installed_inst in instances: - if self.setup['extra']: - self.logger.info("Extra Package %s %s." % \ - (name, self.str_evra(installed_inst))) - tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ - version = installed_inst.get('version'), \ - release = installed_inst.get('release')) - if installed_inst.get('epoch', None) != None: - tmp_entry.set('epoch', str(installed_inst.get('epoch'))) - if installed_inst.get('arch', None) != None: - tmp_entry.set('arch', installed_inst.get('arch')) - extras.append(extra_entry) - return extras - - - def FindExtraInstances(self, pkg_entry, installed_entry): - """ - Check for installed instances that are not in the config. - Return a Package Entry with Instances to remove, or None if there - are no Instances to remove. - - """ - name = pkg_entry.get('name') - extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) - instances = [inst for inst in pkg_entry if inst.tag == 'Instance' or inst.tag == 'Package'] - if name in self.installOnlyPkgs: - for installed_inst in installed_entry: - not_found = True - for inst in instances: - if self.pkg_vr_equal(inst, installed_inst) or \ - self.inst_evra_equal(inst, installed_inst): - not_found = False - break - if not_found == True: - # Extra package. - self.logger.info("Extra InstallOnlyPackage %s %s." % \ - (name, self.str_evra(installed_inst))) - tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ - version = installed_inst.get('version'), \ - release = installed_inst.get('release')) - if installed_inst.get('epoch', None) != None: - tmp_entry.set('epoch', str(installed_inst.get('epoch'))) - if installed_inst.get('arch', None) != None: - tmp_entry.set('arch', installed_inst.get('arch')) - else: - # Normal package, only check arch. - for installed_inst in installed_entry: - not_found = True - for inst in instances: - if installed_inst.get('arch', None) == inst.get('arch', None) or\ - inst.tag == 'Package': - not_found = False - break - if not_found: - self.logger.info("Extra Normal Package Instance %s %s" % \ - (name, self.str_evra(installed_inst))) - tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ - version = installed_inst.get('version'), \ - release = installed_inst.get('release')) - if installed_inst.get('epoch', None) != None: - tmp_entry.set('epoch', str(installed_inst.get('epoch'))) - if installed_inst.get('arch', None) != None: - tmp_entry.set('arch', installed_inst.get('arch')) - - if len(extra_entry) == 0: - extra_entry = None - - return extra_entry - - def str_evra(self, instance): - """Convert evra dict entries to a string.""" - if instance.get('epoch', '*') in ['*', None]: - return '%s-%s.%s' % (instance.get('version', '*'), - instance.get('release', '*'), - instance.get('arch', '*')) - else: - return '%s:%s-%s.%s' % (instance.get('epoch', '*'), - instance.get('version', '*'), - instance.get('release', '*'), - instance.get('arch', '*')) - - def pkg_vr_equal(self, config_entry, installed_entry): - ''' - Compare old style entry to installed entry. Which means ignore - the epoch and arch. - ''' - if (config_entry.tag == 'Package' and \ - config_entry.get('version') == installed_entry.get('version') and \ - config_entry.get('release') == installed_entry.get('release')): - return True - else: - return False - - def inst_evra_equal(self, config_entry, installed_entry): - """Compare new style instance to installed entry.""" - - if config_entry.get('epoch', None) != None: - epoch = int(config_entry.get('epoch')) - else: - epoch = None - - if (config_entry.tag == 'Instance' and \ - (epoch == installed_entry.get('epoch', 0) or \ - (epoch == 0 and installed_entry.get('epoch', 0) == None) or \ - (epoch == None and installed_entry.get('epoch', 0) == 0)) and \ - config_entry.get('version') == installed_entry.get('version') and \ - config_entry.get('release') == installed_entry.get('release') and \ - config_entry.get('arch', None) == installed_entry.get('arch', None)): - return True - else: - return False - - def getinstalledgpg(self): - """ - Create a list of installed GPG key IDs. - - The pgp-pubkey package version is the least significant 4 bytes - (big-endian) of the key ID which is good enough for our purposes. - - """ - init_ts = rpmtools.rpmtransactionset() - init_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) - gpg_hdrs = rpmtools.getheadersbykeyword(init_ts, **{'name':'gpg-pubkey'}) - keyids = [ header[rpm.RPMTAG_VERSION] for header in gpg_hdrs] - keyids.append('None') - init_ts.closeDB() - del init_ts - return keyids - - def VerifyPath(self, entry, _): - """ - We don't do anything here since all - Paths are processed in __init__ - """ - return True +class RPMng(RPM): + """ RPM driver called 'RPMng' for backwards compat """ + deprecated = True diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py new file mode 100644 index 000000000..8a2ba6f87 --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/YUM.py @@ -0,0 +1,951 @@ +"""This provides bcfg2 support for yum.""" + +import copy +import os.path +import sys +import yum +import yum.packages +import yum.rpmtrans +import yum.callbacks +import yum.Errors +import yum.misc +import rpmUtils.arch +import Bcfg2.Client.XML +import Bcfg2.Client.Tools + + +def build_yname(pkgname, inst): + """Build yum appropriate package name.""" + d = {} + if isinstance(inst, yum.packages.PackageObject): + for i in ['name', 'epoch', 'version', 'release', 'arch']: + d[i] = getattr(inst, i) + else: + d['name'] = pkgname + if inst.get('version') != 'any': + d['version'] = inst.get('version') + if inst.get('epoch', False): + d['epoch'] = inst.get('epoch') + if inst.get('release', False) and inst.get('release') != 'any': + d['release'] = inst.get('release') + if inst.get('arch', False) and inst.get('arch') != 'any': + d['arch'] = inst.get('arch') + return d + + +def short_yname(nevra): + d = nevra.copy() + if 'version' in d: + d['ver'] = d['version'] + del d['version'] + if 'release' in d: + d['rel'] = d['release'] + del d['release'] + return d + + +def nevraString(p): + if isinstance(p, yum.packages.PackageObject): + return str(p) + else: + ret = "" + for i, j in [('epoch', '%s:'), ('name', '%s'), ('version', '-%s'), + ('release', '-%s'), ('arch', '.%s')]: + if i in p: + ret = "%s%s" % (ret, j % p[i]) + return ret + + +class RPMDisplay(yum.rpmtrans.RPMBaseCallback): + """We subclass the default RPM transaction callback so that we + can control Yum's verbosity and pipe it through the right logger.""" + + def __init__(self, logger): + yum.rpmtrans.RPMBaseCallback.__init__(self) + self.logger = logger + self.state = None + self.package = None + + def event(self, package, action, te_current, te_total, + ts_current, ts_total): + """ + @param package: A yum package object or simple string of a package name + @param action: A yum.constant transaction set state or in the obscure + rpm repackage case it could be the string 'repackaging' + @param te_current: Current number of bytes processed in the transaction + element being processed + @param te_total: Total number of bytes in the transaction element being + processed + @param ts_current: number of processes completed in whole transaction + @param ts_total: total number of processes in the transaction. + """ + + if self.package != str(package) or action != self.state: + msg = "%s: %s" % (self.action[action], package) + self.logger.info(msg) + self.state = action + self.package = str(package) + + def scriptout(self, package, msgs): + """Handle output from package scripts.""" + + if msgs: + msg = "%s: %s" % (package, msgs) + self.logger.debug(msg) + + def errorlog(self, msg): + """Deal with error reporting.""" + self.logger.error(msg) + + +class YumDisplay(yum.callbacks.ProcessTransBaseCallback): + """Class to handle display of what step we are in the Yum transaction + such as downloading packages, etc.""" + + def __init__(self, logger): + self.logger = logger + + +class YUM(Bcfg2.Client.Tools.PkgTool): + """Support for Yum packages.""" + pkgtype = 'yum' + __execs__ = [] + __handles__ = [('Package', 'yum'), + ('Package', 'rpm'), + ('Path', 'ignore')] + + __req__ = {'Package': ['name'], + 'Path': ['type']} + __ireq__ = {'Package': ['name']} + + conflicts = ['YUM24', 'RPM', 'RPMng', 'YUMng'] + + def __init__(self, logger, setup, config): + self._loadYumBase(setup=setup, logger=logger) + Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) + self.ignores = [entry.get('name') for struct in config \ + for entry in struct \ + if entry.tag == 'Path' and \ + entry.get('type') == 'ignore'] + self.instance_status = {} + self.extra_instances = [] + self.modlists = {} + self._loadConfig() + self.__important__ = self.__important__ + \ + [entry.get('name') for struct in config \ + for entry in struct \ + if entry.tag == 'Path' and \ + (entry.get('name').startswith('/etc/yum.d') \ + or entry.get('name').startswith('/etc/yum.repos.d')) \ + or entry.get('name') == '/etc/yum.conf'] + self.yum_avail = dict() + self.yum_installed = dict() + + yup = self.yb.doPackageLists(pkgnarrow='updates') + if hasattr(self.yb.rpmdb, 'pkglist'): + yinst = self.yb.rpmdb.pkglist + else: + yinst = self.yb.rpmdb.getPkgList() + for dest, source in [(self.yum_avail, yup.updates), + (self.yum_installed, yinst)]: + for pkg in source: + if dest is self.yum_avail: + pname = pkg.name + data = [(pkg.arch, (pkg.epoch, pkg.version, pkg.release))] + else: + pname = pkg[0] + data = [(pkg[1], (pkg[2], pkg[3], pkg[4]))] + if pname in dest: + dest[pname].update(data) + else: + dest[pname] = dict(data) + + def _loadYumBase(self, setup=None, logger=None): + ''' this may be called before PkgTool.__init__() is called on + this object (when the YUM object is first instantiated; + PkgTool.__init__() calls RefreshPackages(), which requires a + YumBase object already exist), or after __init__() has + completed, when we reload the yum config before installing + packages. Consequently, we support both methods by allowing + setup and logger, the only object properties we use in this + function, to be passed as keyword arguments or to be omitted + and drawn from the object itself.''' + self.yb = yum.YumBase() + + if setup is None: + setup = self.setup + if logger is None: + logger = self.logger + + if setup['debug']: + debuglevel = 3 + elif setup['verbose']: + debuglevel = 2 + else: + debuglevel = 0 + + # pylint: disable=E1121 + try: + self.yb.preconf.debuglevel = debuglevel + self.yb._getConfig() + except AttributeError: + self.yb._getConfig(self.yb.conf.config_file_path, + debuglevel=debuglevel) + # pylint: enable=E1121 + + try: + self.yb.doConfigSetup() + self.yb.doTsSetup() + self.yb.doRpmDBSetup() + except yum.Errors.RepoError: + err = sys.exc_info()[1] + logger.error("YUM Repository error: %s" % err) + raise Bcfg2.Client.Tools.ToolInstantiationError + except Exception: + err = sys.exc_info()[1] + logger.error("Yum error: %s" % err) + raise Bcfg2.Client.Tools.ToolInstantiationError + + def _loadConfig(self): + # Process the Yum section from the config file. + # These are all boolean flags, either we do stuff or we don't + self.pkg_checks = self.setup["yum_pkg_checks"] + self.pkg_verify = self.setup["yum_pkg_verify"] + self.doInstall = self.setup["yum_installed_action"] == "install" + self.doUpgrade = self.setup["yum_version_fail_action"] == "upgrade" + self.doReinst = self.setup["yum_verify_fail_action"] == "reinstall" + self.verifyFlags = self.setup["yum_verify_flags"] + + self.installOnlyPkgs = self.yb.conf.installonlypkgs + if 'gpg-pubkey' not in self.installOnlyPkgs: + self.installOnlyPkgs.append('gpg-pubkey') + + self.logger.debug("Yum: Install missing: %s" % self.doInstall) + self.logger.debug("Yum: pkg_checks: %s" % self.pkg_checks) + self.logger.debug("Yum: pkg_verify: %s" % self.pkg_verify) + self.logger.debug("Yum: Upgrade on version fail: %s" % self.doUpgrade) + self.logger.debug("Yum: Reinstall on verify fail: %s" % self.doReinst) + self.logger.debug("Yum: installOnlyPkgs: %s" % self.installOnlyPkgs) + self.logger.debug("Yum: verify_flags: %s" % self.verifyFlags) + + def _fixAutoVersion(self, entry): + # old style entry; synthesize Instances from current installed + if entry.get('name') not in self.yum_installed and \ + entry.get('name') not in self.yum_avail: + # new entry; fall back to default + entry.set('version', 'any') + else: + data = copy.copy(self.yum_installed[entry.get('name')]) + if entry.get('name') in self.yum_avail: + # installed but out of date + data.update(self.yum_avail[entry.get('name')]) + for (arch, (epoch, vers, rel)) in list(data.items()): + x = Bcfg2.Client.XML.SubElement(entry, "Instance", + name=entry.get('name'), + version=vers, arch=arch, + release=rel, epoch=epoch) + if 'verify_flags' in entry.attrib: + x.set('verify_flags', entry.get('verify_flags')) + if 'verify' in entry.attrib: + x.set('verify', entry.get('verify')) + + def _buildInstances(self, entry): + instances = [inst for inst in entry \ + if inst.tag == 'Instance' or inst.tag == 'Package'] + + # XXX: Uniquify instances. Cases where duplicates are returned. + # However, the elements aren't comparable. + + if instances == []: + # We have an old style no Instance entry. Convert it to new style. + instance = Bcfg2.Client.XML.SubElement(entry, 'Package') + for attrib in list(entry.attrib.keys()): + instance.attrib[attrib] = entry.attrib[attrib] + instances = [instance] + + return instances + + def _getGPGKeysAsPackages(self): + """Return a list of the GPG RPM signing keys installed on the + system as a list of Package Objects.""" + + # XXX GPG keys existing in the RPMDB have numbered days + # and newer Yum versions will not return information about them + if hasattr(self.yb.rpmdb, 'returnGPGPubkeyPackages'): + return self.yb.rpmdb.returnGPGPubkeyPackages() + return self.yb.rpmdb.searchNevra(name='gpg-pubkey') + + def _verifyHelper(self, po): + # This code primarly deals with a yum bug where the PO.verify() + # method does not properly take into count multilib sharing of files. + # Neither does RPM proper, really....it just ignores the problem. + def verify(p): + # disabling file checksums is a new feature yum 3.2.17-ish + try: + vResult = p.verify(fast=self.setup.get('quick', False)) + except TypeError: + # Older Yum API + vResult = p.verify() + return vResult + + key = (po.name, po.epoch, po.version, po.release, po.arch) + if key in self.verifyCache: + results = self.verifyCache[key] + else: + results = verify(po) + self.verifyCache[key] = results + if not rpmUtils.arch.isMultiLibArch(): + return results + + # Okay deal with a buggy yum multilib and verify + packages = self.yb.rpmdb.searchNevra(name=po.name, epoch=po.epoch, + ver=po.version, rel=po.release) # find all arches of pkg + if len(packages) == 1: + return results # No mathcing multilib packages + + files = set(po.returnFileEntries()) # Will be the list of common fns + common = {} + for p in packages: + if p != po: + files = files & set(p.returnFileEntries()) + for p in packages: + k = (p.name, p.epoch, p.version, p.release, p.arch) + self.logger.debug("Multilib Verify: comparing %s to %s" \ + % (po, p)) + if k in self.verifyCache: + v = self.verifyCache[k] + else: + v = verify(p) + self.verifyCache[k] = v + + for fn, probs in list(v.items()): + # file problems must exist in ALL multilib packages to be real + if fn in files: + common[fn] = common.get(fn, 0) + 1 + + flag = len(packages) - 1 + for fn, i in list(common.items()): + if i == flag: + # this fn had verify problems in all but one of the multilib + # packages. That means its correct in the package that's + # "on top." Therefore, this is a fake verify problem. + if fn in results: + del results[fn] + + return results + + def RefreshPackages(self): + """ + Creates self.installed{} which is a dict of installed packages. + + The dict items are lists of nevra dicts. This loosely matches the + config from the server and what rpmtools uses to specify pacakges. + + e.g. + + self.installed['foo'] = [ {'name':'foo', 'epoch':None, + 'version':'1', 'release':2, + 'arch':'i386'}, + {'name':'foo', 'epoch':None, + 'version':'1', 'release':2, + 'arch':'x86_64'} ] + """ + + self.installed = {} + packages = self._getGPGKeysAsPackages() + \ + self.yb.rpmdb.returnPackages() + for po in packages: + d = {} + for i in ['name', 'epoch', 'version', 'release', 'arch']: + if i == 'arch' and getattr(po, i) is None: + d[i] = 'noarch' + elif i == 'epoch' and getattr(po, i) is None: + d[i] = '0' + else: + d[i] = getattr(po, i) + self.installed.setdefault(po.name, []).append(d) + + def VerifyPackage(self, entry, modlist, pinned_version=None): + """ + Verify Package status for entry. + Performs the following: + - Checks for the presence of required Package Instances. + - Compares the evra 'version' info against self.installed{}. + - RPM level package verify (rpm --verify). + - Checks for the presence of unrequired package instances. + + Produces the following dict and list for Yum.Install() to use: + For installs/upgrades/fixes of required instances: + instance_status = { <Instance Element Object>: + { 'installed': True|False, + 'version_fail': True|False, + 'verify_fail': True|False, + 'pkg': <Package Element Object>, + 'modlist': [ <filename>, ... ], + 'verify' : [ <rpm --verify results> ] + }, ...... + } + + For deletions of unrequired instances: + extra_instances = [ <Package Element Object>, ..... ] + + Constructs the text prompts for interactive mode. + """ + + if entry.get('version', False) == 'auto': + self._fixAutoVersion(entry) + + self.logger.debug("Verifying package instances for %s" % + entry.get('name')) + + self.verifyCache = {} # Used for checking multilib packages + self.modlists[entry] = modlist + instances = self._buildInstances(entry) + packageCache = [] + package_fail = False + qtext_versions = [] + virtPkg = False + pkg_checks = self.pkg_checks and \ + entry.get('pkg_checks', 'true').lower() == 'true' + pkg_verify = self.pkg_verify and \ + entry.get('pkg_verify', 'true').lower() == 'true' + + if entry.get('name') == 'gpg-pubkey': + POs = self._getGPGKeysAsPackages() + pkg_verify = False # No files here to verify + else: + POs = self.yb.rpmdb.searchNevra(name=entry.get('name')) + if len(POs) == 0: + # Some sort of virtual capability? Try to resolve it + POs = self.yb.rpmdb.searchProvides(entry.get('name')) + if len(POs) > 0: + virtPkg = True + self.logger.info("%s appears to be provided by:" % + entry.get('name')) + for p in POs: + self.logger.info(" %s" % p) + + for inst in instances: + nevra = build_yname(entry.get('name'), inst) + snevra = short_yname(nevra) + if nevra in packageCache: + continue # Ignore duplicate instances + else: + packageCache.append(nevra) + + self.logger.debug("Verifying: %s" % nevraString(nevra)) + + # Set some defaults here + stat = self.instance_status.setdefault(inst, {}) + stat['installed'] = True + stat['version_fail'] = False + stat['verify'] = {} + stat['verify_fail'] = False + stat['pkg'] = entry + stat['modlist'] = modlist + if inst.get('verify_flags'): + # this splits on either space or comma + verify_flags = \ + inst.get('verify_flags').lower().replace(' ', + ',').split(',') + else: + verify_flags = self.verifyFlags + + if 'arch' in nevra: + # If arch is specified use it to select the package + _POs = [ p for p in POs if p.arch == nevra['arch'] ] + else: + _POs = POs + if len(_POs) == 0: + # Package (name, arch) not installed + entry.set('current_exists', 'false') + self.logger.debug(" %s is not installed" % nevraString(nevra)) + stat['installed'] = False + package_fail = True + qtext_versions.append("I(%s)" % nevra) + continue + + if not pkg_checks: + continue + + # Check EVR + if virtPkg: + # we need to make sure that the version of the symbol + # provided matches the one required in the + # configuration + vlist = [] + for attr in ["epoch", "version", "release"]: + vlist.append(nevra.get(attr)) + if tuple(vlist) == (None, None, None): + # we just require the package name, no particular + # version, so just make a copy of POs since every + # package that provides this symbol satisfies the + # requirement + _POs = [po for po in POs] + else: + _POs = [po for po in POs + if po.checkPrco('provides', + (nevra["name"], 'EQ', + tuple(vlist)))] + elif entry.get('name') == 'gpg-pubkey': + if 'version' not in nevra: + m = "Skipping verify: gpg-pubkey without an RPM version." + self.logger.warning(m) + continue + if 'release' not in nevra: + m = "Skipping verify: gpg-pubkey without an RPM release." + self.logger.warning(m) + continue + _POs = [p for p in POs if p.version == nevra['version'] \ + and p.release == nevra['release']] + else: + _POs = self.yb.rpmdb.searchNevra(**snevra) + if len(_POs) == 0: + package_fail = True + stat['version_fail'] = True + # Just chose the first pkg for the error message + if virtPkg: + provTuple = \ + [p for p in POs[0].provides + if p[0] == entry.get("name")][0] + entry.set('current_version', "%s:%s-%s" % provTuple[2]) + self.logger.info(" %s: Wrong version installed. " + "Want %s, but %s provides %s" % + (entry.get("name"), + nevraString(nevra), + nevraString(POs[0]), + yum.misc.prco_tuple_to_string(provTuple))) + else: + entry.set('current_version', "%s:%s-%s.%s" % + (POs[0].epoch, + POs[0].version, + POs[0].release, + POs[0].arch)) + self.logger.info(" %s: Wrong version installed. " + "Want %s, but have %s" % + (entry.get("name"), + nevraString(nevra), + nevraString(POs[0]))) + entry.set('version', "%s:%s-%s.%s" % + (nevra.get('epoch', 'any'), + nevra.get('version', 'any'), + nevra.get('release', 'any'), + nevra.get('arch', 'any'))) + qtext_versions.append("U(%s)" % str(POs[0])) + continue + + if self.setup.get('quick', False): + # Passed -q on the command line + continue + if not (pkg_verify and \ + inst.get('pkg_verify', 'true').lower() == 'true'): + continue + + # XXX: We ignore GPG sig checking the package as it + # has nothing to do with the individual file hash/size/etc. + # GPG checking the package only eaxmines some header/rpmdb + # wacky-ness, and will not properly detect a compromised rpmdb. + # Yum's verify routine does not support it for that reaosn. + + if len(_POs) > 1: + self.logger.debug(" Verify Instance found many packages:") + for po in _POs: + self.logger.debug(" %s" % str(po)) + + try: + vResult = self._verifyHelper(_POs[0]) + except Exception: + e = sys.exc_info()[1] + # Unknown Yum exception + self.logger.warning(" Verify Exception: %s" % str(e)) + package_fail = True + continue + + # Now take out the Yum specific objects / modlists / unproblems + ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ + [ig.get('name') for ig in inst.findall('Ignore')] + \ + self.ignores + for fn, probs in list(vResult.items()): + if fn in modlist: + self.logger.debug(" %s in modlist, skipping" % fn) + continue + if fn in ignores: + self.logger.debug(" %s in ignore list, skipping" % fn) + continue + tmp = [] + for p in probs: + if p.type == 'missing' and os.path.islink(fn): + continue + elif 'no' + p.type in verify_flags: + continue + if p.type not in ['missingok', 'ghost']: + tmp.append((p.type, p.message)) + if tmp != []: + stat['verify'][fn] = tmp + + if stat['verify'] != {}: + stat['verify_fail'] = True + package_fail = True + self.logger.debug("It is suggested that you either manage " + "these files, revert the changes, or ignore " + "false failures:") + self.logger.debug(" Verify Problems:") + for fn, probs in list(stat['verify'].items()): + self.logger.debug(" %s" % fn) + for p in probs: + self.logger.debug(" %s: %s" % p) + + if len(POs) > 0: + # Is this an install only package? We just look at the first one + provides = set([p[0] for p in POs[0].provides] + [POs[0].name]) + install_only = len(set(self.installOnlyPkgs) & provides) > 0 + else: + install_only = False + + if virtPkg or (install_only and not self.setup['kevlar']): + # XXX: virtual capability supplied, we a probably dealing + # with multiple packages of different names. This check + # doesn't make a lot of since in this case + # XXX: install_only: Yum may clean some of these up itself. + # Otherwise having multiple instances of install only packages + # is considered correct + self.extra_instances = None + else: + self.extra_instances = self.FindExtraInstances(entry, POs) + if self.extra_instances is not None: + package_fail = True + + return not package_fail + + def FindExtraInstances(self, entry, POs): + """ + Check for installed instances that are not in the config. + Return a Package Entry with Instances to remove, or None if there + are no Instances to remove. + + """ + if len(POs) == 0: + return None + name = entry.get('name') + extra_entry = Bcfg2.Client.XML.Element('Package', name=name, + type=self.pkgtype) + instances = self._buildInstances(entry) + _POs = [p for p in POs] # Shallow copy + + # Algorythm is sensitive to duplicates, check for them + checked = [] + for inst in instances: + nevra = build_yname(name, inst) + snevra = short_yname(nevra) + pkgs = self.yb.rpmdb.searchNevra(**snevra) + flag = True + if len(pkgs) > 0: + if pkgs[0] in checked: + continue # We've already taken care of this Instance + else: + checked.append(pkgs[0]) + _POs.remove(pkgs[0]) + + for p in _POs: + self.logger.debug(" Extra Instance Found: %s" % str(p)) + Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', + epoch=p.epoch, name=p.name, version=p.version, + release=p.release, arch=p.arch) + + if _POs == []: + return None + else: + return extra_entry + + def FindExtraPackages(self): + """Find extra packages.""" + packages = [e.get('name') for e in self.getSupportedEntries()] + extras = [] + + for p in list(self.installed.keys()): + if p not in packages: + entry = Bcfg2.Client.XML.Element('Package', name=p, + type=self.pkgtype) + for i in self.installed[p]: + inst = Bcfg2.Client.XML.SubElement(entry, + 'Instance', + epoch=i['epoch'], + version=i['version'], + release=i['release'], + arch=i['arch']) + + extras.append(entry) + + return extras + + def _installGPGKey(self, inst, key_file): + """Examine the GPG keys carefully before installation. Avoid + installing duplicate keys. Returns True on successful install.""" + + # RPM Transaction Set + ts = self.yb.rpmdb.readOnlyTS() + + if not os.path.exists(key_file): + self.logger.debug("GPG Key file %s not installed" % key_file) + return False + + rawkey = open(key_file).read() + gpg = yum.misc.getgpgkeyinfo(rawkey) + + ver = yum.misc.keyIdToRPMVer(gpg['keyid']) + rel = yum.misc.keyIdToRPMVer(gpg['timestamp']) + if not (ver == inst.get('version') and rel == inst.get('release')): + self.logger.info("GPG key file %s does not match gpg-pubkey-%s-%s"\ + % (key_file, inst.get('version'), + inst.get('release'))) + return False + + if not yum.misc.keyInstalled(ts, gpg['keyid'], + gpg['timestamp']) == 0: + result = ts.pgpImportPubkey(yum.misc.procgpgkey(rawkey)) + else: + self.logger.debug("gpg-pubkey-%s-%s already installed"\ + % (inst.get('version'), + inst.get('release'))) + return True + + if result != 0: + self.logger.debug("Unable to install %s-%s" % \ + (self.instance_status[inst].get('pkg').get('name'), + nevraString(inst))) + return False + else: + self.logger.debug("Installed %s-%s-%s" % \ + (self.instance_status[inst].get('pkg').get('name'), + inst.get('version'), inst.get('release'))) + return True + + def _runYumTransaction(self): + def cleanup(): + self.yb.closeRpmDB() + self.RefreshPackages() + + rDisplay = RPMDisplay(self.logger) + yDisplay = YumDisplay(self.logger) + # Run the Yum Transaction + try: + rescode, restring = self.yb.buildTransaction() + except yum.Errors.YumBaseError: + e = sys.exc_info()[1] + self.logger.error("Yum transaction error: %s" % str(e)) + cleanup() + return + + self.logger.debug("Initial Yum buildTransaction() run said:") + self.logger.debug(" resultcode: %s, msgs: %s" \ + % (rescode, restring)) + + if rescode != 1: + # Transaction built successfully, run it + try: + self.yb.processTransaction(callback=yDisplay, + rpmDisplay=rDisplay) + self.logger.info("Single Pass for Install Succeeded") + except yum.Errors.YumBaseError: + e = sys.exc_info()[1] + self.logger.error("Yum transaction error: %s" % str(e)) + cleanup() + return + else: + # The yum command failed. No packages installed. + # Try installing instances individually. + self.logger.error("Single Pass Install of Packages Failed") + skipBroken = self.yb.conf.skip_broken + self.yb.conf.skip_broken = True + try: + rescode, restring = self.yb.buildTransaction() + if rescode != 1: + self.yb.processTransaction(callback=yDisplay, + rpmDisplay=rDisplay) + self.logger.debug( + "Second pass install did not install all packages") + else: + self.logger.error("Second pass yum install failed.") + self.logger.debug(" %s" % restring) + except yum.Errors.YumBaseError: + e = sys.exc_info()[1] + self.logger.error("Yum transaction error: %s" % str(e)) + + self.yb.conf.skip_broken = skipBroken + + cleanup() + + def Install(self, packages, states): + """ + Try and fix everything that Yum.VerifyPackages() found wrong for + each Package Entry. This can result in individual RPMs being + installed (for the first time), deleted, downgraded + or upgraded. + + packages is a list of Package Elements that has + states[<Package Element>] == False + + The following effects occur: + - states{} is conditionally updated for each package. + - self.installed{} is rebuilt, possibly multiple times. + - self.instance_status{} is conditionally updated for each instance + of a package. + - Each package will be added to self.modified[] if its states{} + entry is set to True. + + """ + self.logger.debug('Running Yum.Install()') + + install_pkgs = [] + gpg_keys = [] + upgrade_pkgs = [] + reinstall_pkgs = [] + + def queuePkg(pkg, inst, queue): + if pkg.get('name') == 'gpg-pubkey': + gpg_keys.append(inst) + else: + queue.append(inst) + + # Remove extra instances. + # Can not reverify because we don't have a package entry. + if self.extra_instances is not None and len(self.extra_instances) > 0: + if (self.setup.get('remove') == 'all' or \ + self.setup.get('remove') == 'packages'): + self.RemovePackages(self.extra_instances) + else: + self.logger.info("The following extra package instances will be removed by the '-r' option:") + for pkg in self.extra_instances: + for inst in pkg: + self.logger.info(" %s %s" % \ + ((pkg.get('name'), nevraString(inst)))) + + # Figure out which instances of the packages actually need something + # doing to them and place in the appropriate work 'queue'. + for pkg in packages: + insts = [pinst for pinst in pkg \ + if pinst.tag in ['Instance', 'Package']] + if insts: + for inst in insts: + if inst not in self.instance_status: + m = " Asked to install/update package never verified" + p = nevraString(build_yname(pkg.get('name'), inst)) + self.logger.warning("%s: %s" % (m, p)) + continue + status = self.instance_status[inst] + if not status.get('installed', False) and self.doInstall: + queuePkg(pkg, inst, install_pkgs) + elif status.get('version_fail', False) and self.doUpgrade: + queuePkg(pkg, inst, upgrade_pkgs) + elif status.get('verify_fail', False) and self.doReinst: + queuePkg(pkg, inst, reinstall_pkgs) + else: + # Either there was no Install/Version/Verify + # task to be done or the user disabled the actions + # in the configuration. XXX Logging for the latter? + pass + else: + msg = "Yum: Package tag found where Instance expected: %s" + self.logger.warning(msg % pkg.get('name')) + queuePkg(pkg, pkg, install_pkgs) + + # Install GPG keys. + # Alternatively specify the required keys using 'gpgkey' in the + # repository definition in yum.conf. YUM will install the keys + # automatically. + if len(gpg_keys) > 0: + self.logger.info("Installing GPG keys.") + for inst in gpg_keys: + if inst.get('simplefile') is None: + self.logger.error("GPG key has no simplefile attribute") + continue + key_file = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ + inst.get('simplefile')) + self._installGPGKey(inst, key_file) + + self.RefreshPackages() + pkg = self.instance_status[gpg_keys[0]].get('pkg') + states[pkg] = self.VerifyPackage(pkg, []) + + # We want to reload all Yum configuration in case we've + # deployed new .repo files we should consider + self._loadYumBase() + + # Install packages. + if len(install_pkgs) > 0: + self.logger.info("Attempting to install packages") + + for inst in install_pkgs: + pkg_arg = self.instance_status[inst].get('pkg').get('name') + self.logger.debug("Installing %s" % pkg_arg) + try: + self.yb.install(**build_yname(pkg_arg, inst)) + except yum.Errors.YumBaseError: + yume = sys.exc_info()[1] + self.logger.error("Error installing package %s: %s" % + (pkg_arg, yume)) + + if len(upgrade_pkgs) > 0: + self.logger.info("Attempting to upgrade packages") + + for inst in upgrade_pkgs: + pkg_arg = self.instance_status[inst].get('pkg').get('name') + self.logger.debug("Upgrading %s" % pkg_arg) + try: + self.yb.update(**build_yname(pkg_arg, inst)) + except yum.Errors.YumBaseError: + yume = sys.exc_info()[1] + self.logger.error("Error upgrading package %s: %s" % + (pkg_arg, yume)) + + if len(reinstall_pkgs) > 0: + self.logger.info("Attempting to reinstall packages") + for inst in reinstall_pkgs: + pkg_arg = self.instance_status[inst].get('pkg').get('name') + self.logger.debug("Reinstalling %s" % pkg_arg) + try: + self.yb.reinstall(**build_yname(pkg_arg, inst)) + except yum.Errors.YumBaseError: + yume = sys.exc_info()[1] + self.logger.error("Error reinstalling package %s: %s" % + (pkg_arg, yume)) + + self._runYumTransaction() + + if not self.setup['kevlar']: + for pkg_entry in [p for p in packages if self.canVerify(p)]: + self.logger.debug("Reverifying Failed Package %s" \ + % (pkg_entry.get('name'))) + states[pkg_entry] = self.VerifyPackage(pkg_entry, + self.modlists.get(pkg_entry, [])) + + for entry in [ent for ent in packages if states[ent]]: + self.modified.append(entry) + + def RemovePackages(self, packages): + """ + Remove specified entries. + + packages is a list of Package Entries with Instances generated + by FindExtraPackages(). + """ + self.logger.debug('Running Yum.RemovePackages()') + + erase_args = [] + for pkg in packages: + for inst in pkg: + nevra = build_yname(pkg.get('name'), inst) + if pkg.get('name') != 'gpg-pubkey': + self.yb.remove(**nevra) + self.modified.append(pkg) + else: + self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s-%s"\ + % (nevra['name'], nevra['version'], nevra['release'])) + self.logger.info(" This package will be deleted in a future version of the Yum driver.") + + self._runYumTransaction() + self.extra = self.FindExtraPackages() + + def VerifyPath(self, entry, _): + """Do nothing here since we only verify Path type=ignore""" + return True diff --git a/src/lib/Bcfg2/Client/Tools/YUM24.py b/src/lib/Bcfg2/Client/Tools/YUM24.py index 2bc821db3..9107d7a0d 100644 --- a/src/lib/Bcfg2/Client/Tools/YUM24.py +++ b/src/lib/Bcfg2/Client/Tools/YUM24.py @@ -5,10 +5,7 @@ import os.path import sys import yum import Bcfg2.Client.XML -import Bcfg2.Client.Tools.RPMng - -if not hasattr(Bcfg2.Client.Tools.RPMng, 'RPMng'): - raise ImportError +from Bcfg2.Client.Tools.RPM import RPM def build_yname(pkgname, inst): @@ -27,11 +24,10 @@ def build_yname(pkgname, inst): return ypname -class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): +class YUM24(RPM): """Support for Yum packages.""" pkgtype = 'yum' - - name = 'YUM24' + deprecated = True __execs__ = ['/usr/bin/yum', '/var/lib/rpm'] __handles__ = [('Package', 'yum'), ('Package', 'rpm'), @@ -57,7 +53,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): 'Instance': ['version', 'release']} def __init__(self, logger, setup, config): - Bcfg2.Client.Tools.RPMng.RPMng.__init__(self, logger, setup, config) + RPM.__init__(self, logger, setup, config) self.__important__ = self.__important__ + \ [entry.get('name') for struct in config \ for entry in struct \ @@ -140,15 +136,15 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): if len(pkgDict) > 1: # What do we do with multiple packages? - s = "YUMng: returnPackagesByDep(%s) returned many packages" + s = "YUM24: returnPackagesByDep(%s) returned many packages" self.logger.info(s % entry.get('name')) - s = "YUMng: matching packages: %s" + s = "YUM24: matching packages: %s" self.logger.info(s % str(list(pkgDict.keys()))) pkgs = set(pkgDict.keys()) & set(self.yum_installed.keys()) if len(pkgs) > 0: # Virtual packages matches an installed real package pkg = pkgDict[pkgs.pop()] - s = "YUMng: chosing: %s" % pkg.name + s = "YUM24: chosing: %s" % pkg.name self.logger.info(s) else: # What's the right package? This will fail verify @@ -157,21 +153,20 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): elif len(pkgDict) == 1: pkg = list(pkgDict.values())[0] else: # len(pkgDict) == 0 - s = "YUMng: returnPackagesByDep(%s) returned no results" + s = "YUM24: returnPackagesByDep(%s) returned no results" self.logger.info(s % entry.get('name')) pkg = None if pkg is not None: - s = "YUMng: remapping virtual package %s to %s" + s = "YUM24: remapping virtual package %s to %s" self.logger.info(s % (entry.get('name'), pkg.name)) entry.set('name', pkg.name) - return Bcfg2.Client.Tools.RPMng.RPMng.VerifyPackage(self, entry, - modlist) + return RPM.VerifyPackage(self, entry, modlist) def Install(self, packages, states): """ - Try and fix everything that RPMng.VerifyPackages() found wrong for + Try and fix everything that YUM24.VerifyPackages() found wrong for each Package Entry. This can result in individual RPMs being installed (for the first time), deleted, downgraded or upgraded. @@ -191,7 +186,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): entry is set to True. """ - self.logger.info('Running YUMng.Install()') + self.logger.info('Running YUM24.Install()') install_pkgs = [] gpg_keys = [] @@ -344,7 +339,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): packages is a list of Package Entries with Instances generated by FindExtraPackages(). """ - self.logger.debug('Running YUMng.RemovePackages()') + self.logger.debug('Running YUM24.RemovePackages()') if self.autodep: pkgtool = "/usr/bin/yum -d0 -y erase %s" @@ -368,7 +363,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): 'release': inst.get('release')} self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkgspec.get('name'), self.str_evra(pkgspec))) - self.logger.info(" This package will be deleted in a future version of the RPMng driver.") + self.logger.info(" This package will be deleted in a future version of the YUM24 driver.") cmdrc, output = self.cmd.run(pkgtool % " ".join(erase_args)) if cmdrc == 0: @@ -392,7 +387,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng): else: self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkg.get('name'), self.str_evra(pkg))) - self.logger.info(" This package will be deleted in a future version of the RPMng driver.") + self.logger.info(" This package will be deleted in a future version of the YUM24 driver.") continue cmdrc, output = self.cmd.run(self.pkgtool % pkg_arg) diff --git a/src/lib/Bcfg2/Client/Tools/YUMng.py b/src/lib/Bcfg2/Client/Tools/YUMng.py index a93a48f9a..22fbba537 100644 --- a/src/lib/Bcfg2/Client/Tools/YUMng.py +++ b/src/lib/Bcfg2/Client/Tools/YUMng.py @@ -1,952 +1,9 @@ -"""This provides bcfg2 support for yum.""" +""" YUM driver called 'YUMng' for backwards compat """ -import copy -import os.path -import sys -import yum -import yum.packages -import yum.rpmtrans -import yum.callbacks -import yum.Errors -import yum.misc -import rpmUtils.arch -import Bcfg2.Client.XML -import Bcfg2.Client.Tools +from Bcfg2.Client.Tools.YUM import YUM -def build_yname(pkgname, inst): - """Build yum appropriate package name.""" - d = {} - if isinstance(inst, yum.packages.PackageObject): - for i in ['name', 'epoch', 'version', 'release', 'arch']: - d[i] = getattr(inst, i) - else: - d['name'] = pkgname - if inst.get('version') != 'any': - d['version'] = inst.get('version') - if inst.get('epoch', False): - d['epoch'] = inst.get('epoch') - if inst.get('release', False) and inst.get('release') != 'any': - d['release'] = inst.get('release') - if inst.get('arch', False) and inst.get('arch') != 'any': - d['arch'] = inst.get('arch') - return d - -def short_yname(nevra): - d = nevra.copy() - if 'version' in d: - d['ver'] = d['version'] - del d['version'] - if 'release' in d: - d['rel'] = d['release'] - del d['release'] - return d - - -def nevraString(p): - if isinstance(p, yum.packages.PackageObject): - return str(p) - else: - ret = "" - for i, j in [('epoch', '%s:'), ('name', '%s'), ('version', '-%s'), - ('release', '-%s'), ('arch', '.%s')]: - if i in p: - ret = "%s%s" % (ret, j % p[i]) - return ret - - -class RPMDisplay(yum.rpmtrans.RPMBaseCallback): - """We subclass the default RPM transaction callback so that we - can control Yum's verbosity and pipe it through the right logger.""" - - def __init__(self, logger): - yum.rpmtrans.RPMBaseCallback.__init__(self) - self.logger = logger - self.state = None - self.package = None - - def event(self, package, action, te_current, te_total, - ts_current, ts_total): - """ - @param package: A yum package object or simple string of a package name - @param action: A yum.constant transaction set state or in the obscure - rpm repackage case it could be the string 'repackaging' - @param te_current: Current number of bytes processed in the transaction - element being processed - @param te_total: Total number of bytes in the transaction element being - processed - @param ts_current: number of processes completed in whole transaction - @param ts_total: total number of processes in the transaction. - """ - - if self.package != str(package) or action != self.state: - msg = "%s: %s" % (self.action[action], package) - self.logger.info(msg) - self.state = action - self.package = str(package) - - def scriptout(self, package, msgs): - """Handle output from package scripts.""" - - if msgs: - msg = "%s: %s" % (package, msgs) - self.logger.debug(msg) - - def errorlog(self, msg): - """Deal with error reporting.""" - self.logger.error(msg) - - -class YumDisplay(yum.callbacks.ProcessTransBaseCallback): - """Class to handle display of what step we are in the Yum transaction - such as downloading packages, etc.""" - - def __init__(self, logger): - self.logger = logger - - -class YUMng(Bcfg2.Client.Tools.PkgTool): - """Support for Yum packages.""" - pkgtype = 'yum' - - name = 'YUMng' - __execs__ = [] - __handles__ = [('Package', 'yum'), - ('Package', 'rpm'), - ('Path', 'ignore')] - - __req__ = {'Package': ['name'], - 'Path': ['type']} - __ireq__ = {'Package': ['name']} - - conflicts = ['YUM24', 'RPMng'] - - def __init__(self, logger, setup, config): - self._loadYumBase(setup=setup, logger=logger) - Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) - self.ignores = [entry.get('name') for struct in config \ - for entry in struct \ - if entry.tag == 'Path' and \ - entry.get('type') == 'ignore'] - self.instance_status = {} - self.extra_instances = [] - self.modlists = {} - self._loadConfig() - self.__important__ = self.__important__ + \ - [entry.get('name') for struct in config \ - for entry in struct \ - if entry.tag == 'Path' and \ - (entry.get('name').startswith('/etc/yum.d') \ - or entry.get('name').startswith('/etc/yum.repos.d')) \ - or entry.get('name') == '/etc/yum.conf'] - self.yum_avail = dict() - self.yum_installed = dict() - - yup = self.yb.doPackageLists(pkgnarrow='updates') - if hasattr(self.yb.rpmdb, 'pkglist'): - yinst = self.yb.rpmdb.pkglist - else: - yinst = self.yb.rpmdb.getPkgList() - for dest, source in [(self.yum_avail, yup.updates), - (self.yum_installed, yinst)]: - for pkg in source: - if dest is self.yum_avail: - pname = pkg.name - data = [(pkg.arch, (pkg.epoch, pkg.version, pkg.release))] - else: - pname = pkg[0] - data = [(pkg[1], (pkg[2], pkg[3], pkg[4]))] - if pname in dest: - dest[pname].update(data) - else: - dest[pname] = dict(data) - - def _loadYumBase(self, setup=None, logger=None): - ''' this may be called before PkgTool.__init__() is called on - this object (when the YUMng object is first instantiated; - PkgTool.__init__() calls RefreshPackages(), which requires a - YumBase object already exist), or after __init__() has - completed, when we reload the yum config before installing - packages. Consequently, we support both methods by allowing - setup and logger, the only object properties we use in this - function, to be passed as keyword arguments or to be omitted - and drawn from the object itself.''' - self.yb = yum.YumBase() - - if setup is None: - setup = self.setup - if logger is None: - logger = self.logger - - if setup['debug']: - debuglevel = 3 - elif setup['verbose']: - debuglevel = 2 - else: - debuglevel = 0 - - # pylint: disable=E1121 - try: - self.yb.preconf.debuglevel = debuglevel - self.yb._getConfig() - except AttributeError: - self.yb._getConfig(self.yb.conf.config_file_path, - debuglevel=debuglevel) - # pylint: enable=E1121 - - try: - self.yb.doConfigSetup() - self.yb.doTsSetup() - self.yb.doRpmDBSetup() - except yum.Errors.RepoError: - err = sys.exc_info()[1] - logger.error("YUMng Repository error: %s" % err) - raise Bcfg2.Client.Tools.toolInstantiationError - except Exception: - err = sys.exc_info()[1] - logger.error("YUMng error: %s" % err) - raise Bcfg2.Client.Tools.toolInstantiationError - - def _loadConfig(self): - # Process the YUMng section from the config file. - # These are all boolean flags, either we do stuff or we don't - self.pkg_checks = self.setup["yumng_pkg_checks"] - self.pkg_verify = self.setup["yumng_pkg_verify"] - self.doInstall = self.setup["yumng_installed_action"] == "install" - self.doUpgrade = self.setup["yumng_version_fail_action"] == "upgrade" - self.doReinst = self.setup["yumng_verify_fail_action"] == "reinstall" - self.verifyFlags = self.setup["yumng_verify_flags"] - - self.installOnlyPkgs = self.yb.conf.installonlypkgs - if 'gpg-pubkey' not in self.installOnlyPkgs: - self.installOnlyPkgs.append('gpg-pubkey') - - self.logger.debug("YUMng: Install missing: %s" % self.doInstall) - self.logger.debug("YUMng: pkg_checks: %s" % self.pkg_checks) - self.logger.debug("YUMng: pkg_verify: %s" % self.pkg_verify) - self.logger.debug("YUMng: Upgrade on version fail: %s" % self.doUpgrade) - self.logger.debug("YUMng: Reinstall on verify fail: %s" % self.doReinst) - self.logger.debug("YUMng: installOnlyPkgs: %s" % self.installOnlyPkgs) - self.logger.debug("YUMng: verify_flags: %s" % self.verifyFlags) - - def _fixAutoVersion(self, entry): - # old style entry; synthesize Instances from current installed - if entry.get('name') not in self.yum_installed and \ - entry.get('name') not in self.yum_avail: - # new entry; fall back to default - entry.set('version', 'any') - else: - data = copy.copy(self.yum_installed[entry.get('name')]) - if entry.get('name') in self.yum_avail: - # installed but out of date - data.update(self.yum_avail[entry.get('name')]) - for (arch, (epoch, vers, rel)) in list(data.items()): - x = Bcfg2.Client.XML.SubElement(entry, "Instance", - name=entry.get('name'), - version=vers, arch=arch, - release=rel, epoch=epoch) - if 'verify_flags' in entry.attrib: - x.set('verify_flags', entry.get('verify_flags')) - if 'verify' in entry.attrib: - x.set('verify', entry.get('verify')) - - def _buildInstances(self, entry): - instances = [inst for inst in entry \ - if inst.tag == 'Instance' or inst.tag == 'Package'] - - # XXX: Uniquify instances. Cases where duplicates are returned. - # However, the elements aren't comparable. - - if instances == []: - # We have an old style no Instance entry. Convert it to new style. - instance = Bcfg2.Client.XML.SubElement(entry, 'Package') - for attrib in list(entry.attrib.keys()): - instance.attrib[attrib] = entry.attrib[attrib] - instances = [instance] - - return instances - - def _getGPGKeysAsPackages(self): - """Return a list of the GPG RPM signing keys installed on the - system as a list of Package Objects.""" - - # XXX GPG keys existing in the RPMDB have numbered days - # and newer Yum versions will not return information about them - if hasattr(self.yb.rpmdb, 'returnGPGPubkeyPackages'): - return self.yb.rpmdb.returnGPGPubkeyPackages() - return self.yb.rpmdb.searchNevra(name='gpg-pubkey') - - def _verifyHelper(self, po): - # This code primarly deals with a yum bug where the PO.verify() - # method does not properly take into count multilib sharing of files. - # Neither does RPM proper, really....it just ignores the problem. - def verify(p): - # disabling file checksums is a new feature yum 3.2.17-ish - try: - vResult = p.verify(fast=self.setup.get('quick', False)) - except TypeError: - # Older Yum API - vResult = p.verify() - return vResult - - key = (po.name, po.epoch, po.version, po.release, po.arch) - if key in self.verifyCache: - results = self.verifyCache[key] - else: - results = verify(po) - self.verifyCache[key] = results - if not rpmUtils.arch.isMultiLibArch(): - return results - - # Okay deal with a buggy yum multilib and verify - packages = self.yb.rpmdb.searchNevra(name=po.name, epoch=po.epoch, - ver=po.version, rel=po.release) # find all arches of pkg - if len(packages) == 1: - return results # No mathcing multilib packages - - files = set(po.returnFileEntries()) # Will be the list of common fns - common = {} - for p in packages: - if p != po: - files = files & set(p.returnFileEntries()) - for p in packages: - k = (p.name, p.epoch, p.version, p.release, p.arch) - self.logger.debug("Multilib Verify: comparing %s to %s" \ - % (po, p)) - if k in self.verifyCache: - v = self.verifyCache[k] - else: - v = verify(p) - self.verifyCache[k] = v - - for fn, probs in list(v.items()): - # file problems must exist in ALL multilib packages to be real - if fn in files: - common[fn] = common.get(fn, 0) + 1 - - flag = len(packages) - 1 - for fn, i in list(common.items()): - if i == flag: - # this fn had verify problems in all but one of the multilib - # packages. That means its correct in the package that's - # "on top." Therefore, this is a fake verify problem. - if fn in results: - del results[fn] - - return results - - def RefreshPackages(self): - """ - Creates self.installed{} which is a dict of installed packages. - - The dict items are lists of nevra dicts. This loosely matches the - config from the server and what rpmtools uses to specify pacakges. - - e.g. - - self.installed['foo'] = [ {'name':'foo', 'epoch':None, - 'version':'1', 'release':2, - 'arch':'i386'}, - {'name':'foo', 'epoch':None, - 'version':'1', 'release':2, - 'arch':'x86_64'} ] - """ - - self.installed = {} - packages = self._getGPGKeysAsPackages() + \ - self.yb.rpmdb.returnPackages() - for po in packages: - d = {} - for i in ['name', 'epoch', 'version', 'release', 'arch']: - if i == 'arch' and getattr(po, i) is None: - d[i] = 'noarch' - elif i == 'epoch' and getattr(po, i) is None: - d[i] = '0' - else: - d[i] = getattr(po, i) - self.installed.setdefault(po.name, []).append(d) - - def VerifyPackage(self, entry, modlist, pinned_version=None): - """ - Verify Package status for entry. - Performs the following: - - Checks for the presence of required Package Instances. - - Compares the evra 'version' info against self.installed{}. - - RPM level package verify (rpm --verify). - - Checks for the presence of unrequired package instances. - - Produces the following dict and list for YUMng.Install() to use: - For installs/upgrades/fixes of required instances: - instance_status = { <Instance Element Object>: - { 'installed': True|False, - 'version_fail': True|False, - 'verify_fail': True|False, - 'pkg': <Package Element Object>, - 'modlist': [ <filename>, ... ], - 'verify' : [ <rpm --verify results> ] - }, ...... - } - - For deletions of unrequired instances: - extra_instances = [ <Package Element Object>, ..... ] - - Constructs the text prompts for interactive mode. - """ - - if entry.get('version', False) == 'auto': - self._fixAutoVersion(entry) - - self.logger.debug("Verifying package instances for %s" % - entry.get('name')) - - self.verifyCache = {} # Used for checking multilib packages - self.modlists[entry] = modlist - instances = self._buildInstances(entry) - packageCache = [] - package_fail = False - qtext_versions = [] - virtPkg = False - pkg_checks = self.pkg_checks and \ - entry.get('pkg_checks', 'true').lower() == 'true' - pkg_verify = self.pkg_verify and \ - entry.get('pkg_verify', 'true').lower() == 'true' - - if entry.get('name') == 'gpg-pubkey': - POs = self._getGPGKeysAsPackages() - pkg_verify = False # No files here to verify - else: - POs = self.yb.rpmdb.searchNevra(name=entry.get('name')) - if len(POs) == 0: - # Some sort of virtual capability? Try to resolve it - POs = self.yb.rpmdb.searchProvides(entry.get('name')) - if len(POs) > 0: - virtPkg = True - self.logger.info("%s appears to be provided by:" % - entry.get('name')) - for p in POs: - self.logger.info(" %s" % p) - - for inst in instances: - nevra = build_yname(entry.get('name'), inst) - snevra = short_yname(nevra) - if nevra in packageCache: - continue # Ignore duplicate instances - else: - packageCache.append(nevra) - - self.logger.debug("Verifying: %s" % nevraString(nevra)) - - # Set some defaults here - stat = self.instance_status.setdefault(inst, {}) - stat['installed'] = True - stat['version_fail'] = False - stat['verify'] = {} - stat['verify_fail'] = False - stat['pkg'] = entry - stat['modlist'] = modlist - if inst.get('verify_flags'): - # this splits on either space or comma - verify_flags = \ - inst.get('verify_flags').lower().replace(' ', - ',').split(',') - else: - verify_flags = self.verifyFlags - - if 'arch' in nevra: - # If arch is specified use it to select the package - _POs = [ p for p in POs if p.arch == nevra['arch'] ] - else: - _POs = POs - if len(_POs) == 0: - # Package (name, arch) not installed - entry.set('current_exists', 'false') - self.logger.debug(" %s is not installed" % nevraString(nevra)) - stat['installed'] = False - package_fail = True - qtext_versions.append("I(%s)" % nevra) - continue - - if not pkg_checks: - continue - - # Check EVR - if virtPkg: - # we need to make sure that the version of the symbol - # provided matches the one required in the - # configuration - vlist = [] - for attr in ["epoch", "version", "release"]: - vlist.append(nevra.get(attr)) - if tuple(vlist) == (None, None, None): - # we just require the package name, no particular - # version, so just make a copy of POs since every - # package that provides this symbol satisfies the - # requirement - _POs = [po for po in POs] - else: - _POs = [po for po in POs - if po.checkPrco('provides', - (nevra["name"], 'EQ', - tuple(vlist)))] - elif entry.get('name') == 'gpg-pubkey': - if 'version' not in nevra: - m = "Skipping verify: gpg-pubkey without an RPM version." - self.logger.warning(m) - continue - if 'release' not in nevra: - m = "Skipping verify: gpg-pubkey without an RPM release." - self.logger.warning(m) - continue - _POs = [p for p in POs if p.version == nevra['version'] \ - and p.release == nevra['release']] - else: - _POs = self.yb.rpmdb.searchNevra(**snevra) - if len(_POs) == 0: - package_fail = True - stat['version_fail'] = True - # Just chose the first pkg for the error message - if virtPkg: - provTuple = \ - [p for p in POs[0].provides - if p[0] == entry.get("name")][0] - entry.set('current_version', "%s:%s-%s" % provTuple[2]) - self.logger.info(" %s: Wrong version installed. " - "Want %s, but %s provides %s" % - (entry.get("name"), - nevraString(nevra), - nevraString(POs[0]), - yum.misc.prco_tuple_to_string(provTuple))) - else: - entry.set('current_version', "%s:%s-%s.%s" % - (POs[0].epoch, - POs[0].version, - POs[0].release, - POs[0].arch)) - self.logger.info(" %s: Wrong version installed. " - "Want %s, but have %s" % - (entry.get("name"), - nevraString(nevra), - nevraString(POs[0]))) - entry.set('version', "%s:%s-%s.%s" % - (nevra.get('epoch', 'any'), - nevra.get('version', 'any'), - nevra.get('release', 'any'), - nevra.get('arch', 'any'))) - qtext_versions.append("U(%s)" % str(POs[0])) - continue - - if self.setup.get('quick', False): - # Passed -q on the command line - continue - if not (pkg_verify and \ - inst.get('pkg_verify', 'true').lower() == 'true'): - continue - - # XXX: We ignore GPG sig checking the package as it - # has nothing to do with the individual file hash/size/etc. - # GPG checking the package only eaxmines some header/rpmdb - # wacky-ness, and will not properly detect a compromised rpmdb. - # Yum's verify routine does not support it for that reaosn. - - if len(_POs) > 1: - self.logger.debug(" Verify Instance found many packages:") - for po in _POs: - self.logger.debug(" %s" % str(po)) - - try: - vResult = self._verifyHelper(_POs[0]) - except Exception: - e = sys.exc_info()[1] - # Unknown Yum exception - self.logger.warning(" Verify Exception: %s" % str(e)) - package_fail = True - continue - - # Now take out the Yum specific objects / modlists / unproblems - ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ - [ig.get('name') for ig in inst.findall('Ignore')] + \ - self.ignores - for fn, probs in list(vResult.items()): - if fn in modlist: - self.logger.debug(" %s in modlist, skipping" % fn) - continue - if fn in ignores: - self.logger.debug(" %s in ignore list, skipping" % fn) - continue - tmp = [] - for p in probs: - if p.type == 'missing' and os.path.islink(fn): - continue - elif 'no' + p.type in verify_flags: - continue - if p.type not in ['missingok', 'ghost']: - tmp.append((p.type, p.message)) - if tmp != []: - stat['verify'][fn] = tmp - - if stat['verify'] != {}: - stat['verify_fail'] = True - package_fail = True - self.logger.debug("It is suggested that you either manage " - "these files, revert the changes, or ignore " - "false failures:") - self.logger.debug(" Verify Problems:") - for fn, probs in list(stat['verify'].items()): - self.logger.debug(" %s" % fn) - for p in probs: - self.logger.debug(" %s: %s" % p) - - if len(POs) > 0: - # Is this an install only package? We just look at the first one - provides = set([p[0] for p in POs[0].provides] + [POs[0].name]) - install_only = len(set(self.installOnlyPkgs) & provides) > 0 - else: - install_only = False - - if virtPkg or (install_only and not self.setup['kevlar']): - # XXX: virtual capability supplied, we a probably dealing - # with multiple packages of different names. This check - # doesn't make a lot of since in this case - # XXX: install_only: Yum may clean some of these up itself. - # Otherwise having multiple instances of install only packages - # is considered correct - self.extra_instances = None - else: - self.extra_instances = self.FindExtraInstances(entry, POs) - if self.extra_instances is not None: - package_fail = True - - return not package_fail - - def FindExtraInstances(self, entry, POs): - """ - Check for installed instances that are not in the config. - Return a Package Entry with Instances to remove, or None if there - are no Instances to remove. - - """ - if len(POs) == 0: - return None - name = entry.get('name') - extra_entry = Bcfg2.Client.XML.Element('Package', name=name, - type=self.pkgtype) - instances = self._buildInstances(entry) - _POs = [p for p in POs] # Shallow copy - - # Algorythm is sensitive to duplicates, check for them - checked = [] - for inst in instances: - nevra = build_yname(name, inst) - snevra = short_yname(nevra) - pkgs = self.yb.rpmdb.searchNevra(**snevra) - flag = True - if len(pkgs) > 0: - if pkgs[0] in checked: - continue # We've already taken care of this Instance - else: - checked.append(pkgs[0]) - _POs.remove(pkgs[0]) - - for p in _POs: - self.logger.debug(" Extra Instance Found: %s" % str(p)) - Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', - epoch=p.epoch, name=p.name, version=p.version, - release=p.release, arch=p.arch) - - if _POs == []: - return None - else: - return extra_entry - - def FindExtraPackages(self): - """Find extra packages.""" - packages = [e.get('name') for e in self.getSupportedEntries()] - extras = [] - - for p in list(self.installed.keys()): - if p not in packages: - entry = Bcfg2.Client.XML.Element('Package', name=p, - type=self.pkgtype) - for i in self.installed[p]: - inst = Bcfg2.Client.XML.SubElement(entry, - 'Instance', - epoch=i['epoch'], - version=i['version'], - release=i['release'], - arch=i['arch']) - - extras.append(entry) - - return extras - - def _installGPGKey(self, inst, key_file): - """Examine the GPG keys carefully before installation. Avoid - installing duplicate keys. Returns True on successful install.""" - - # RPM Transaction Set - ts = self.yb.rpmdb.readOnlyTS() - - if not os.path.exists(key_file): - self.logger.debug("GPG Key file %s not installed" % key_file) - return False - - rawkey = open(key_file).read() - gpg = yum.misc.getgpgkeyinfo(rawkey) - - ver = yum.misc.keyIdToRPMVer(gpg['keyid']) - rel = yum.misc.keyIdToRPMVer(gpg['timestamp']) - if not (ver == inst.get('version') and rel == inst.get('release')): - self.logger.info("GPG key file %s does not match gpg-pubkey-%s-%s"\ - % (key_file, inst.get('version'), - inst.get('release'))) - return False - - if not yum.misc.keyInstalled(ts, gpg['keyid'], - gpg['timestamp']) == 0: - result = ts.pgpImportPubkey(yum.misc.procgpgkey(rawkey)) - else: - self.logger.debug("gpg-pubkey-%s-%s already installed"\ - % (inst.get('version'), - inst.get('release'))) - return True - - if result != 0: - self.logger.debug("Unable to install %s-%s" % \ - (self.instance_status[inst].get('pkg').get('name'), - nevraString(inst))) - return False - else: - self.logger.debug("Installed %s-%s-%s" % \ - (self.instance_status[inst].get('pkg').get('name'), - inst.get('version'), inst.get('release'))) - return True - - def _runYumTransaction(self): - def cleanup(): - self.yb.closeRpmDB() - self.RefreshPackages() - - rDisplay = RPMDisplay(self.logger) - yDisplay = YumDisplay(self.logger) - # Run the Yum Transaction - try: - rescode, restring = self.yb.buildTransaction() - except yum.Errors.YumBaseError: - e = sys.exc_info()[1] - self.logger.error("Yum transaction error: %s" % str(e)) - cleanup() - return - - self.logger.debug("Initial Yum buildTransaction() run said:") - self.logger.debug(" resultcode: %s, msgs: %s" \ - % (rescode, restring)) - - if rescode != 1: - # Transaction built successfully, run it - try: - self.yb.processTransaction(callback=yDisplay, - rpmDisplay=rDisplay) - self.logger.info("Single Pass for Install Succeeded") - except yum.Errors.YumBaseError: - e = sys.exc_info()[1] - self.logger.error("Yum transaction error: %s" % str(e)) - cleanup() - return - else: - # The yum command failed. No packages installed. - # Try installing instances individually. - self.logger.error("Single Pass Install of Packages Failed") - skipBroken = self.yb.conf.skip_broken - self.yb.conf.skip_broken = True - try: - rescode, restring = self.yb.buildTransaction() - if rescode != 1: - self.yb.processTransaction(callback=yDisplay, - rpmDisplay=rDisplay) - self.logger.debug( - "Second pass install did not install all packages") - else: - self.logger.error("Second pass yum install failed.") - self.logger.debug(" %s" % restring) - except yum.Errors.YumBaseError: - e = sys.exc_info()[1] - self.logger.error("Yum transaction error: %s" % str(e)) - - self.yb.conf.skip_broken = skipBroken - - cleanup() - - def Install(self, packages, states): - """ - Try and fix everything that YUMng.VerifyPackages() found wrong for - each Package Entry. This can result in individual RPMs being - installed (for the first time), deleted, downgraded - or upgraded. - - packages is a list of Package Elements that has - states[<Package Element>] == False - - The following effects occur: - - states{} is conditionally updated for each package. - - self.installed{} is rebuilt, possibly multiple times. - - self.instance_status{} is conditionally updated for each instance - of a package. - - Each package will be added to self.modified[] if its states{} - entry is set to True. - - """ - self.logger.debug('Running YUMng.Install()') - - install_pkgs = [] - gpg_keys = [] - upgrade_pkgs = [] - reinstall_pkgs = [] - - def queuePkg(pkg, inst, queue): - if pkg.get('name') == 'gpg-pubkey': - gpg_keys.append(inst) - else: - queue.append(inst) - - # Remove extra instances. - # Can not reverify because we don't have a package entry. - if self.extra_instances is not None and len(self.extra_instances) > 0: - if (self.setup.get('remove') == 'all' or \ - self.setup.get('remove') == 'packages'): - self.RemovePackages(self.extra_instances) - else: - self.logger.info("The following extra package instances will be removed by the '-r' option:") - for pkg in self.extra_instances: - for inst in pkg: - self.logger.info(" %s %s" % \ - ((pkg.get('name'), nevraString(inst)))) - - # Figure out which instances of the packages actually need something - # doing to them and place in the appropriate work 'queue'. - for pkg in packages: - insts = [pinst for pinst in pkg \ - if pinst.tag in ['Instance', 'Package']] - if insts: - for inst in insts: - if inst not in self.instance_status: - m = " Asked to install/update package never verified" - p = nevraString(build_yname(pkg.get('name'), inst)) - self.logger.warning("%s: %s" % (m, p)) - continue - status = self.instance_status[inst] - if not status.get('installed', False) and self.doInstall: - queuePkg(pkg, inst, install_pkgs) - elif status.get('version_fail', False) and self.doUpgrade: - queuePkg(pkg, inst, upgrade_pkgs) - elif status.get('verify_fail', False) and self.doReinst: - queuePkg(pkg, inst, reinstall_pkgs) - else: - # Either there was no Install/Version/Verify - # task to be done or the user disabled the actions - # in the configuration. XXX Logging for the latter? - pass - else: - msg = "YUMng: Package tag found where Instance expected: %s" - self.logger.warning(msg % pkg.get('name')) - queuePkg(pkg, pkg, install_pkgs) - - # Install GPG keys. - # Alternatively specify the required keys using 'gpgkey' in the - # repository definition in yum.conf. YUM will install the keys - # automatically. - if len(gpg_keys) > 0: - self.logger.info("Installing GPG keys.") - for inst in gpg_keys: - if inst.get('simplefile') is None: - self.logger.error("GPG key has no simplefile attribute") - continue - key_file = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ - inst.get('simplefile')) - self._installGPGKey(inst, key_file) - - self.RefreshPackages() - pkg = self.instance_status[gpg_keys[0]].get('pkg') - states[pkg] = self.VerifyPackage(pkg, []) - - # We want to reload all Yum configuration in case we've - # deployed new .repo files we should consider - self._loadYumBase() - - # Install packages. - if len(install_pkgs) > 0: - self.logger.info("Attempting to install packages") - - for inst in install_pkgs: - pkg_arg = self.instance_status[inst].get('pkg').get('name') - self.logger.debug("Installing %s" % pkg_arg) - try: - self.yb.install(**build_yname(pkg_arg, inst)) - except yum.Errors.YumBaseError: - yume = sys.exc_info()[1] - self.logger.error("Error installing package %s: %s" % - (pkg_arg, yume)) - - if len(upgrade_pkgs) > 0: - self.logger.info("Attempting to upgrade packages") - - for inst in upgrade_pkgs: - pkg_arg = self.instance_status[inst].get('pkg').get('name') - self.logger.debug("Upgrading %s" % pkg_arg) - try: - self.yb.update(**build_yname(pkg_arg, inst)) - except yum.Errors.YumBaseError: - yume = sys.exc_info()[1] - self.logger.error("Error upgrading package %s: %s" % - (pkg_arg, yume)) - - if len(reinstall_pkgs) > 0: - self.logger.info("Attempting to reinstall packages") - for inst in reinstall_pkgs: - pkg_arg = self.instance_status[inst].get('pkg').get('name') - self.logger.debug("Reinstalling %s" % pkg_arg) - try: - self.yb.reinstall(**build_yname(pkg_arg, inst)) - except yum.Errors.YumBaseError: - yume = sys.exc_info()[1] - self.logger.error("Error reinstalling package %s: %s" % - (pkg_arg, yume)) - - self._runYumTransaction() - - if not self.setup['kevlar']: - for pkg_entry in [p for p in packages if self.canVerify(p)]: - self.logger.debug("Reverifying Failed Package %s" \ - % (pkg_entry.get('name'))) - states[pkg_entry] = self.VerifyPackage(pkg_entry, - self.modlists.get(pkg_entry, [])) - - for entry in [ent for ent in packages if states[ent]]: - self.modified.append(entry) - - def RemovePackages(self, packages): - """ - Remove specified entries. - - packages is a list of Package Entries with Instances generated - by FindExtraPackages(). - """ - self.logger.debug('Running YUMng.RemovePackages()') - - erase_args = [] - for pkg in packages: - for inst in pkg: - nevra = build_yname(pkg.get('name'), inst) - if pkg.get('name') != 'gpg-pubkey': - self.yb.remove(**nevra) - self.modified.append(pkg) - else: - self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s-%s"\ - % (nevra['name'], nevra['version'], nevra['release'])) - self.logger.info(" This package will be deleted in a future version of the YUMng driver.") - - self._runYumTransaction() - self.extra = self.FindExtraPackages() - - def VerifyPath(self, entry, _): - """Do nothing here since we only verify Path type=ignore""" - return True +class YUMng(YUM): + """ YUM driver called 'YUMng' for backwards compat """ + deprecated = True + conflicts = ['YUM24', 'RPM', 'RPMng'] diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py index 51d3ceecf..c11b96ef7 100644 --- a/src/lib/Bcfg2/Client/Tools/__init__.py +++ b/src/lib/Bcfg2/Client/Tools/__init__.py @@ -42,6 +42,7 @@ class Tool(object): __handles__ = [] __req__ = {} __important__ = [] + deprecated = False def __init__(self, logger, setup, config): self.setup = setup @@ -144,7 +145,7 @@ class Tool(object): required.extend(self.__req__[entry.tag][entry.get("type")]) except KeyError: pass - + return [attr for attr in required if attr not in entry.attrib or not entry.attrib[attr]] diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index f1bc54d49..04233f165 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -776,55 +776,63 @@ CLIENT_PORTAGE_BINPKGONLY = \ default=False, cf=('Portage', 'binpkgonly'), cook=get_bool) -CLIENT_RPMNG_INSTALLONLY = \ - Option('RPMng install-only packages', +CLIENT_RPM_INSTALLONLY = \ + Option('RPM install-only packages', default=['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp', 'kernel-modules', 'kernel-debug', 'kernel-unsupported', 'kernel-devel', 'kernel-source', 'kernel-default', 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], - cf=('RPMng', 'installonlypackages'), + cf=('RPM', 'installonlypackages'), + deprecated_cf=('RPMng', 'installonlypackages'), cook=list_split) -CLIENT_RPMNG_PKG_CHECKS = \ - Option("Perform RPMng package checks", +CLIENT_RPM_PKG_CHECKS = \ + Option("Perform RPM package checks", default=True, - cf=('RPMng', 'pkg_checks'), + cf=('RPM', 'pkg_checks'), + deprecated_cf=('RPMng', 'pkg_checks'), cook=get_bool) -CLIENT_RPMNG_PKG_VERIFY = \ - Option("Perform RPMng package verify", +CLIENT_RPM_PKG_VERIFY = \ + Option("Perform RPM package verify", default=True, - cf=('RPMng', 'pkg_verify'), + cf=('RPM', 'pkg_verify'), + deprecated_cf=('RPMng', 'pkg_verify'), cook=get_bool) -CLIENT_RPMNG_INSTALLED_ACTION = \ - Option("RPMng installed action", +CLIENT_RPM_INSTALLED_ACTION = \ + Option("RPM installed action", default="install", - cf=('RPMng', 'installed_action')) -CLIENT_RPMNG_ERASE_FLAGS = \ - Option("RPMng erase flags", + cf=('RPM', 'installed_action'), + deprecated_cf=('RPMng', 'installed_action')) +CLIENT_RPM_ERASE_FLAGS = \ + Option("RPM erase flags", default=["allmatches"], - cf=('RPMng', 'erase_flags'), + cf=('RPM', 'erase_flags'), + deprecated_cf=('RPMng', 'erase_flags'), cook=list_split) -CLIENT_RPMNG_VERSION_FAIL_ACTION = \ - Option("RPMng version fail action", +CLIENT_RPM_VERSION_FAIL_ACTION = \ + Option("RPM version fail action", default="upgrade", - cf=('RPMng', 'version_fail_action')) -CLIENT_RPMNG_VERIFY_FAIL_ACTION = \ - Option("RPMng verify fail action", + cf=('RPM', 'version_fail_action'), + deprecated_cf=('RPMng', 'version_fail_action')) +CLIENT_RPM_VERIFY_FAIL_ACTION = \ + Option("RPM verify fail action", default="reinstall", - cf=('RPMng', 'verify_fail_action')) -CLIENT_RPMNG_VERIFY_FLAGS = \ - Option("RPMng verify flags", + cf=('RPM', 'verify_fail_action'), + deprecated_cf=('RPMng', 'verify_fail_action')) +CLIENT_RPM_VERIFY_FLAGS = \ + Option("RPM verify flags", default=[], - cf=('RPMng', 'verify_flags'), + cf=('RPM', 'verify_flags'), + deprecated_cf=('RPMng', 'verify_flags'), cook=list_split) CLIENT_YUM24_INSTALLONLY = \ - Option('RPMng install-only packages', + Option('YUM24 install-only packages', default=['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp', 'kernel-modules', 'kernel-debug', 'kernel-unsupported', 'kernel-devel', 'kernel-source', 'kernel-default', 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], - cf=('RPMng', 'installonlypackages'), + cf=('YUM24', 'installonlypackages'), cook=list_split) CLIENT_YUM24_PKG_CHECKS = \ Option("Perform YUM24 package checks", @@ -863,32 +871,38 @@ CLIENT_YUM24_AUTODEP = \ default=True, cf=('YUM24', 'autodep'), cook=get_bool) -CLIENT_YUMNG_PKG_CHECKS = \ - Option("Perform YUMng package checks", +CLIENT_YUM_PKG_CHECKS = \ + Option("Perform YUM package checks", default=True, - cf=('YUMng', 'pkg_checks'), + cf=('YUM', 'pkg_checks'), + deprecated_cf=('YUMng', 'pkg_checks'), cook=get_bool) -CLIENT_YUMNG_PKG_VERIFY = \ - Option("Perform YUMng package verify", +CLIENT_YUM_PKG_VERIFY = \ + Option("Perform YUM package verify", default=True, - cf=('YUMng', 'pkg_verify'), + cf=('YUM', 'pkg_verify'), + deprecated_cf=('YUMng', 'pkg_verify'), cook=get_bool) -CLIENT_YUMNG_INSTALLED_ACTION = \ - Option("YUMng installed action", +CLIENT_YUM_INSTALLED_ACTION = \ + Option("YUM installed action", default="install", - cf=('YUMng', 'installed_action')) -CLIENT_YUMNG_VERSION_FAIL_ACTION = \ - Option("YUMng version fail action", + cf=('YUM', 'installed_action'), + deprecated_cf=('YUMng', 'installed_action')) +CLIENT_YUM_VERSION_FAIL_ACTION = \ + Option("YUM version fail action", default="upgrade", - cf=('YUMng', 'version_fail_action')) -CLIENT_YUMNG_VERIFY_FAIL_ACTION = \ - Option("YUMng verify fail action", + cf=('YUM', 'version_fail_action'), + deprecated_cf=('YUMng', 'version_fail_action')) +CLIENT_YUM_VERIFY_FAIL_ACTION = \ + Option("YUM verify fail action", default="reinstall", - cf=('YUMng', 'verify_fail_action')) -CLIENT_YUMNG_VERIFY_FLAGS = \ - Option("YUMng verify flags", + cf=('YUM', 'verify_fail_action'), + deprecated_cf=('YUMng', 'verify_fail_action')) +CLIENT_YUM_VERIFY_FLAGS = \ + Option("YUM verify flags", default=[], - cf=('YUMng', 'verify_flags'), + cf=('YUM', 'verify_flags'), + deprecated_cf=('YUMng', 'verify_flags'), cook=list_split) # Logging options @@ -1008,14 +1022,14 @@ DRIVER_OPTIONS = \ apt_var_path=CLIENT_APT_TOOLS_VAR_PATH, apt_etc_path=CLIENT_SYSTEM_ETC_PATH, portage_binpkgonly=CLIENT_PORTAGE_BINPKGONLY, - rpmng_installonly=CLIENT_RPMNG_INSTALLONLY, - rpmng_pkg_checks=CLIENT_RPMNG_PKG_CHECKS, - rpmng_pkg_verify=CLIENT_RPMNG_PKG_VERIFY, - rpmng_installed_action=CLIENT_RPMNG_INSTALLED_ACTION, - rpmng_erase_flags=CLIENT_RPMNG_ERASE_FLAGS, - rpmng_version_fail_action=CLIENT_RPMNG_VERSION_FAIL_ACTION, - rpmng_verify_fail_action=CLIENT_RPMNG_VERIFY_FAIL_ACTION, - rpmng_verify_flags=CLIENT_RPMNG_VERIFY_FLAGS, + rpm_installonly=CLIENT_RPM_INSTALLONLY, + rpm_pkg_checks=CLIENT_RPM_PKG_CHECKS, + rpm_pkg_verify=CLIENT_RPM_PKG_VERIFY, + rpm_installed_action=CLIENT_RPM_INSTALLED_ACTION, + rpm_erase_flags=CLIENT_RPM_ERASE_FLAGS, + rpm_version_fail_action=CLIENT_RPM_VERSION_FAIL_ACTION, + rpm_verify_fail_action=CLIENT_RPM_VERIFY_FAIL_ACTION, + rpm_verify_flags=CLIENT_RPM_VERIFY_FLAGS, yum24_installonly=CLIENT_YUM24_INSTALLONLY, yum24_pkg_checks=CLIENT_YUM24_PKG_CHECKS, yum24_pkg_verify=CLIENT_YUM24_PKG_VERIFY, @@ -1025,12 +1039,12 @@ DRIVER_OPTIONS = \ yum24_verify_fail_action=CLIENT_YUM24_VERIFY_FAIL_ACTION, yum24_verify_flags=CLIENT_YUM24_VERIFY_FLAGS, yum24_autodep=CLIENT_YUM24_AUTODEP, - yumng_pkg_checks=CLIENT_YUMNG_PKG_CHECKS, - yumng_pkg_verify=CLIENT_YUMNG_PKG_VERIFY, - yumng_installed_action=CLIENT_YUMNG_INSTALLED_ACTION, - yumng_version_fail_action=CLIENT_YUMNG_VERSION_FAIL_ACTION, - yumng_verify_fail_action=CLIENT_YUMNG_VERIFY_FAIL_ACTION, - yumng_verify_flags=CLIENT_YUMNG_VERIFY_FLAGS) + yum_pkg_checks=CLIENT_YUM_PKG_CHECKS, + yum_pkg_verify=CLIENT_YUM_PKG_VERIFY, + yum_installed_action=CLIENT_YUM_INSTALLED_ACTION, + yum_version_fail_action=CLIENT_YUM_VERSION_FAIL_ACTION, + yum_verify_fail_action=CLIENT_YUM_VERIFY_FAIL_ACTION, + yum_verify_flags=CLIENT_YUM_VERIFY_FLAGS) CLIENT_COMMON_OPTIONS = \ dict(extra=CLIENT_EXTRA_DISPLAY, |