PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/src/lib/Client/Tools/RPMng.py

https://github.com/ab/bcfg2-old
Python | 937 lines | 764 code | 59 blank | 114 comment | 176 complexity | 9d144764f45c3c682579ceda95ba9fb6 MD5 | raw file
  1. """Bcfg2 Support for RPMS"""
  2. __revision__ = '$Revision$'
  3. import os.path
  4. import rpm
  5. import rpmtools
  6. import Bcfg2.Client.Tools
  7. # Compatibility import
  8. from Bcfg2.Bcfg2Py3k import ConfigParser
  9. # Fix for python2.3
  10. try:
  11. set
  12. except NameError:
  13. from sets import Set as set
  14. class RPMng(Bcfg2.Client.Tools.PkgTool):
  15. """Support for RPM packages."""
  16. name = 'RPMng'
  17. __execs__ = ['/bin/rpm', '/var/lib/rpm']
  18. __handles__ = [('Package', 'rpm')]
  19. __req__ = {'Package': ['name', 'version']}
  20. __ireq__ = {'Package': ['url']}
  21. __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']}
  22. __new_ireq__ = {'Package': ['uri'], \
  23. 'Instance': ['simplefile']}
  24. __gpg_req__ = {'Package': ['name', 'version']}
  25. __gpg_ireq__ = {'Package': ['name', 'version']}
  26. __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']}
  27. __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']}
  28. conflicts = ['RPM']
  29. pkgtype = 'rpm'
  30. pkgtool = ("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"]))
  31. def __init__(self, logger, setup, config):
  32. Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config)
  33. # create a global ignore list used when ignoring particular
  34. # files during package verification
  35. self.ignores = [entry.get('name') for struct in config for entry in struct \
  36. if entry.get('type') == 'ignore']
  37. self.instance_status = {}
  38. self.extra_instances = []
  39. self.modlists = {}
  40. self.gpg_keyids = self.getinstalledgpg()
  41. # Process thee RPMng section from the config file.
  42. RPMng_CP = ConfigParser.ConfigParser()
  43. RPMng_CP.read(self.setup.get('setup'))
  44. # installonlypackages
  45. self.installOnlyPkgs = []
  46. if RPMng_CP.has_option(self.name, 'installonlypackages'):
  47. for i in RPMng_CP.get(self.name, 'installonlypackages').split(','):
  48. self.installOnlyPkgs.append(i.strip())
  49. if self.installOnlyPkgs == []:
  50. self.installOnlyPkgs = ['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp',
  51. 'kernel-modules', 'kernel-debug', 'kernel-unsupported',
  52. 'kernel-source', 'kernel-devel', 'kernel-default',
  53. 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen',
  54. 'gpg-pubkey']
  55. if 'gpg-pubkey' not in self.installOnlyPkgs:
  56. self.installOnlyPkgs.append('gpg-pubkey')
  57. self.logger.debug('installOnlyPackages = %s' % self.installOnlyPkgs)
  58. # erase_flags
  59. self.erase_flags = []
  60. if RPMng_CP.has_option(self.name, 'erase_flags'):
  61. for i in RPMng_CP.get(self.name, 'erase_flags').split(','):
  62. self.erase_flags.append(i.strip())
  63. if self.erase_flags == []:
  64. self.erase_flags = ['allmatches']
  65. self.logger.debug('erase_flags = %s' % self.erase_flags)
  66. # pkg_checks
  67. if RPMng_CP.has_option(self.name, 'pkg_checks'):
  68. self.pkg_checks = RPMng_CP.get(self.name, 'pkg_checks').lower()
  69. else:
  70. self.pkg_checks = 'true'
  71. self.logger.debug('pkg_checks = %s' % self.pkg_checks)
  72. # pkg_verify
  73. if RPMng_CP.has_option(self.name, 'pkg_verify'):
  74. self.pkg_verify = RPMng_CP.get(self.name, 'pkg_verify').lower()
  75. else:
  76. self.pkg_verify = 'true'
  77. self.logger.debug('pkg_verify = %s' % self.pkg_verify)
  78. # installed_action
  79. if RPMng_CP.has_option(self.name, 'installed_action'):
  80. self.installed_action = RPMng_CP.get(self.name, 'installed_action').lower()
  81. else:
  82. self.installed_action = 'install'
  83. self.logger.debug('installed_action = %s' % self.installed_action)
  84. # version_fail_action
  85. if RPMng_CP.has_option(self.name, 'version_fail_action'):
  86. self.version_fail_action = RPMng_CP.get(self.name, 'version_fail_action').lower()
  87. else:
  88. self.version_fail_action = 'upgrade'
  89. self.logger.debug('version_fail_action = %s' % self.version_fail_action)
  90. # verify_fail_action
  91. if self.name == "RPMng":
  92. if RPMng_CP.has_option(self.name, 'verify_fail_action'):
  93. self.verify_fail_action = RPMng_CP.get(self.name, 'verify_fail_action').lower()
  94. else:
  95. self.verify_fail_action = 'reinstall'
  96. else: # yum can't reinstall packages.
  97. self.verify_fail_action = 'none'
  98. self.logger.debug('verify_fail_action = %s' % self.verify_fail_action)
  99. # version_fail_action
  100. if RPMng_CP.has_option(self.name, 'verify_flags'):
  101. self.verify_flags = RPMng_CP.get(self.name, 'verify_flags').lower().split(',')
  102. else:
  103. self.verify_flags = []
  104. if '' in self.verify_flags:
  105. self.verify_flags.remove('')
  106. self.logger.debug('version_fail_action = %s' % self.version_fail_action)
  107. # Force a re- prelink of all packages if prelink exists.
  108. # Many, if not most package verifies can be caused by out of date prelinking.
  109. if os.path.isfile('/usr/sbin/prelink') and not self.setup['dryrun']:
  110. cmdrc, output = self.cmd.run('/usr/sbin/prelink -a -mR')
  111. if cmdrc == 0:
  112. self.logger.debug('Pre-emptive prelink succeeded')
  113. else:
  114. # FIXME : this is dumb - what if the output is huge?
  115. self.logger.error('Pre-emptive prelink failed: %s' % output)
  116. def RefreshPackages(self):
  117. """
  118. Creates self.installed{} which is a dict of installed packages.
  119. The dict items are lists of nevra dicts. This loosely matches the
  120. config from the server and what rpmtools uses to specify pacakges.
  121. e.g.
  122. self.installed['foo'] = [ {'name':'foo', 'epoch':None,
  123. 'version':'1', 'release':2,
  124. 'arch':'i386'},
  125. {'name':'foo', 'epoch':None,
  126. 'version':'1', 'release':2,
  127. 'arch':'x86_64'} ]
  128. """
  129. self.installed = {}
  130. refresh_ts = rpmtools.rpmtransactionset()
  131. # Don't bother with signature checks at this stage. The GPG keys might
  132. # not be installed.
  133. refresh_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES)
  134. for nevra in rpmtools.rpmpackagelist(refresh_ts):
  135. self.installed.setdefault(nevra['name'], []).append(nevra)
  136. if self.setup['debug']:
  137. print("The following package instances are installed:")
  138. for name, instances in list(self.installed.items()):
  139. self.logger.debug(" " + name)
  140. for inst in instances:
  141. self.logger.debug(" %s" %self.str_evra(inst))
  142. refresh_ts.closeDB()
  143. del refresh_ts
  144. def VerifyPackage(self, entry, modlist, pinned_version=None):
  145. """
  146. Verify Package status for entry.
  147. Performs the following:
  148. - Checks for the presence of required Package Instances.
  149. - Compares the evra 'version' info against self.installed{}.
  150. - RPM level package verify (rpm --verify).
  151. - Checks for the presence of unrequired package instances.
  152. Produces the following dict and list for RPMng.Install() to use:
  153. For installs/upgrades/fixes of required instances:
  154. instance_status = { <Instance Element Object>:
  155. { 'installed': True|False,
  156. 'version_fail': True|False,
  157. 'verify_fail': True|False,
  158. 'pkg': <Package Element Object>,
  159. 'modlist': [ <filename>, ... ],
  160. 'verify' : [ <rpm --verify results> ]
  161. }, ......
  162. }
  163. For deletions of unrequired instances:
  164. extra_instances = [ <Package Element Object>, ..... ]
  165. Constructs the text prompts for interactive mode.
  166. """
  167. instances = [inst for inst in entry if inst.tag == 'Instance' or inst.tag == 'Package']
  168. if instances == []:
  169. # We have an old style no Instance entry. Convert it to new style.
  170. instance = Bcfg2.Client.XML.SubElement(entry, 'Package')
  171. for attrib in list(entry.attrib.keys()):
  172. instance.attrib[attrib] = entry.attrib[attrib]
  173. if self.pkg_checks == 'true' and entry.get('pkg_checks', 'true') == 'true':
  174. if 'any' in [entry.get('version'), pinned_version]:
  175. version, release = 'any', 'any'
  176. elif entry.get('version') == 'auto':
  177. if pinned_version != None:
  178. version, release = pinned_version.split('-')
  179. else:
  180. return False
  181. else:
  182. version, release = entry.get('version').split('-')
  183. instance.set('version', version)
  184. instance.set('release', release)
  185. if entry.get('verify', 'true') == 'false':
  186. instance.set('verify', 'false')
  187. instances = [ instance ]
  188. self.logger.debug("Verifying package instances for %s" % entry.get('name'))
  189. package_fail = False
  190. qtext_versions = ''
  191. if entry.get('name') in self.installed:
  192. # There is at least one instance installed.
  193. if self.pkg_checks == 'true' and entry.get('pkg_checks', 'true') == 'true':
  194. rpmTs = rpm.TransactionSet()
  195. rpmHeader = None
  196. for h in rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')):
  197. if rpmHeader is None or rpm.versionCompare(h, rpmHeader) > 0:
  198. rpmHeader = h
  199. rpmProvides = [ h['provides'] for h in \
  200. rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')) ]
  201. rpmIntersection = set(rpmHeader['provides']) & \
  202. set(self.installOnlyPkgs)
  203. if len(rpmIntersection) > 0:
  204. # Packages that should only be installed or removed.
  205. # e.g. kernels.
  206. self.logger.debug(" Install only package.")
  207. for inst in instances:
  208. self.instance_status.setdefault(inst, {})['installed'] = False
  209. self.instance_status[inst]['version_fail'] = False
  210. if inst.tag == 'Package' and len(self.installed[entry.get('name')]) > 1:
  211. self.logger.error("WARNING: Multiple instances of package %s are installed." % \
  212. (entry.get('name')))
  213. for pkg in self.installed[entry.get('name')]:
  214. if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) \
  215. or self.inst_evra_equal(inst, pkg):
  216. if inst.get('version') == 'any':
  217. self.logger.error("got any version")
  218. self.logger.debug(" %s" % self.str_evra(inst))
  219. self.instance_status[inst]['installed'] = True
  220. if self.pkg_verify == 'true' and \
  221. inst.get('pkg_verify', 'true') == 'true':
  222. flags = inst.get('verify_flags', '').split(',') + self.verify_flags
  223. if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \
  224. entry.get('name') != 'gpg-pubkey':
  225. flags += ['nosignature', 'nodigest']
  226. self.logger.debug('WARNING: Package %s %s requires GPG Public key with ID %s'\
  227. % (pkg.get('name'), self.str_evra(pkg), \
  228. pkg.get('gpgkeyid', '')))
  229. self.logger.debug(' Disabling signature check.')
  230. if self.setup.get('quick', False):
  231. if rpmtools.prelink_exists:
  232. flags += ['nomd5', 'nosize']
  233. else:
  234. flags += ['nomd5']
  235. self.logger.debug(" verify_flags = %s" % flags)
  236. if inst.get('verify', 'true') == 'false':
  237. self.instance_status[inst]['verify'] = None
  238. else:
  239. vp_ts = rpmtools.rpmtransactionset()
  240. self.instance_status[inst]['verify'] = \
  241. rpmtools.rpm_verify( vp_ts, pkg, flags)
  242. vp_ts.closeDB()
  243. del vp_ts
  244. if self.instance_status[inst]['installed'] == False:
  245. self.logger.info(" Package %s %s not installed." % \
  246. (entry.get('name'), self.str_evra(inst)))
  247. qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst)
  248. entry.set('current_exists', 'false')
  249. else:
  250. # Normal Packages that can be upgraded.
  251. for inst in instances:
  252. self.instance_status.setdefault(inst, {})['installed'] = False
  253. self.instance_status[inst]['version_fail'] = False
  254. # Only installed packages with the same architecture are
  255. # relevant.
  256. if inst.get('arch', None) == None:
  257. arch_match = self.installed[entry.get('name')]
  258. else:
  259. arch_match = [pkg for pkg in self.installed[entry.get('name')] \
  260. if pkg.get('arch', None) == inst.get('arch', None)]
  261. if len(arch_match) > 1:
  262. self.logger.error("Multiple instances of package %s installed with the same achitecture." % \
  263. (entry.get('name')))
  264. elif len(arch_match) == 1:
  265. # There is only one installed like there should be.
  266. # Check that it is the right version.
  267. for pkg in arch_match:
  268. if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) or \
  269. self.inst_evra_equal(inst, pkg):
  270. self.logger.debug(" %s" % self.str_evra(inst))
  271. self.instance_status[inst]['installed'] = True
  272. if self.pkg_verify == 'true' and \
  273. inst.get('pkg_verify', 'true') == 'true':
  274. flags = inst.get('verify_flags', '').split(',') + self.verify_flags
  275. if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \
  276. 'nosignature' not in flags:
  277. flags += ['nosignature', 'nodigest']
  278. self.logger.info('WARNING: Package %s %s requires GPG Public key with ID %s'\
  279. % (pkg.get('name'), self.str_evra(pkg), \
  280. pkg.get('gpgkeyid', '')))
  281. self.logger.info(' Disabling signature check.')
  282. if self.setup.get('quick', False):
  283. if rpmtools.prelink_exists:
  284. flags += ['nomd5', 'nosize']
  285. else:
  286. flags += ['nomd5']
  287. self.logger.debug(" verify_flags = %s" % flags)
  288. if inst.get('verify', 'true') == 'false':
  289. self.instance_status[inst]['verify'] = None
  290. else:
  291. vp_ts = rpmtools.rpmtransactionset()
  292. self.instance_status[inst]['verify'] = \
  293. rpmtools.rpm_verify( vp_ts, pkg, flags )
  294. vp_ts.closeDB()
  295. del vp_ts
  296. else:
  297. # Wrong version installed.
  298. self.instance_status[inst]['version_fail'] = True
  299. self.logger.info(" Wrong version installed. Want %s, but have %s"\
  300. % (self.str_evra(inst), self.str_evra(pkg)))
  301. qtext_versions = qtext_versions + 'U(%s -> %s) ' % \
  302. (self.str_evra(pkg), self.str_evra(inst))
  303. elif len(arch_match) == 0:
  304. # This instance is not installed.
  305. self.instance_status[inst]['installed'] = False
  306. self.logger.info(" %s is not installed." % self.str_evra(inst))
  307. qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst)
  308. # Check the rpm verify results.
  309. for inst in instances:
  310. instance_fail = False
  311. # Dump the rpm verify results.
  312. #****Write something to format this nicely.*****
  313. if self.setup['debug'] and self.instance_status[inst].get('verify', None):
  314. self.logger.debug(self.instance_status[inst]['verify'])
  315. self.instance_status[inst]['verify_fail'] = False
  316. if self.instance_status[inst].get('verify', None):
  317. if len(self.instance_status[inst].get('verify')) > 1:
  318. self.logger.info("WARNING: Verification of more than one package instance.")
  319. for result in self.instance_status[inst]['verify']:
  320. # Check header results
  321. if result.get('hdr', None):
  322. instance_fail = True
  323. self.instance_status[inst]['verify_fail'] = True
  324. # Check dependency results
  325. if result.get('deps', None):
  326. instance_fail = True
  327. self.instance_status[inst]['verify_fail'] = True
  328. # Check the rpm verify file results against the modlist
  329. # and entry and per Instance Ignores.
  330. ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \
  331. [ig.get('name') for ig in inst.findall('Ignore')] + \
  332. self.ignores
  333. for file_result in result.get('files', []):
  334. if file_result[-1] not in modlist + ignores:
  335. instance_fail = True
  336. self.instance_status[inst]['verify_fail'] = True
  337. else:
  338. self.logger.debug(" Modlist/Ignore match: %s" % \
  339. (file_result[-1]))
  340. if instance_fail == True:
  341. self.logger.debug("*** Instance %s failed RPM verification ***" % \
  342. self.str_evra(inst))
  343. qtext_versions = qtext_versions + 'R(%s) ' % self.str_evra(inst)
  344. self.modlists[entry] = modlist
  345. # Attach status structure for return to server for reporting.
  346. inst.set('verify_status', str(self.instance_status[inst]))
  347. if self.instance_status[inst]['installed'] == False or \
  348. self.instance_status[inst].get('version_fail', False)== True or \
  349. self.instance_status[inst].get('verify_fail', False) == True:
  350. package_fail = True
  351. self.instance_status[inst]['pkg'] = entry
  352. self.modlists[entry] = modlist
  353. # Find Installed Instances that are not in the Config.
  354. extra_installed = self.FindExtraInstances(entry, self.installed[entry.get('name')])
  355. if extra_installed != None:
  356. package_fail = True
  357. self.extra_instances.append(extra_installed)
  358. for inst in extra_installed.findall('Instance'):
  359. qtext_versions = qtext_versions + 'D(%s) ' % self.str_evra(inst)
  360. self.logger.debug("Found Extra Instances %s" % qtext_versions)
  361. if package_fail == True:
  362. self.logger.info(" Package %s failed verification." % \
  363. (entry.get('name')))
  364. qtext = 'Install/Upgrade/delete Package %s instance(s) - %s (y/N) ' % \
  365. (entry.get('name'), qtext_versions)
  366. entry.set('qtext', qtext)
  367. bcfg2_versions = ''
  368. for bcfg2_inst in [inst for inst in instances if inst.tag == 'Instance']:
  369. bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(bcfg2_inst)
  370. if bcfg2_versions != '':
  371. entry.set('version', bcfg2_versions)
  372. installed_versions = ''
  373. for installed_inst in self.installed[entry.get('name')]:
  374. installed_versions = installed_versions + '(%s) ' % \
  375. self.str_evra(installed_inst)
  376. entry.set('current_version', installed_versions)
  377. return False
  378. else:
  379. # There are no Instances of this package installed.
  380. self.logger.debug("Package %s has no instances installed" % (entry.get('name')))
  381. entry.set('current_exists', 'false')
  382. bcfg2_versions = ''
  383. for inst in instances:
  384. qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst)
  385. self.instance_status.setdefault(inst, {})['installed'] = False
  386. self.modlists[entry] = modlist
  387. self.instance_status[inst]['pkg'] = entry
  388. if inst.tag == 'Instance':
  389. bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(inst)
  390. if bcfg2_versions != '':
  391. entry.set('version', bcfg2_versions)
  392. entry.set('qtext', "Install Package %s Instance(s) %s? (y/N) " % \
  393. (entry.get('name'), qtext_versions))
  394. return False
  395. return True
  396. def RemovePackages(self, packages):
  397. """
  398. Remove specified entries.
  399. packages is a list of Package Entries with Instances generated
  400. by FindExtraPackages().
  401. """
  402. self.logger.debug('Running RPMng.RemovePackages()')
  403. pkgspec_list = []
  404. for pkg in packages:
  405. for inst in pkg:
  406. if pkg.get('name') != 'gpg-pubkey':
  407. pkgspec = { 'name':pkg.get('name'),
  408. 'epoch':inst.get('epoch', None),
  409. 'version':inst.get('version'),
  410. 'release':inst.get('release'),
  411. 'arch':inst.get('arch') }
  412. pkgspec_list.append(pkgspec)
  413. else:
  414. pkgspec = { 'name':pkg.get('name'),
  415. 'version':inst.get('version'),
  416. 'release':inst.get('release')}
  417. self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
  418. % (pkgspec.get('name'), self.str_evra(pkgspec)))
  419. self.logger.info(" This package will be deleted in a future version of the RPMng driver.")
  420. #pkgspec_list.append(pkg_spec)
  421. erase_results = rpmtools.rpm_erase(pkgspec_list, self.erase_flags)
  422. if erase_results == []:
  423. self.modified += packages
  424. for pkg in pkgspec_list:
  425. self.logger.info("Deleted %s %s" % (pkg.get('name'), self.str_evra(pkg)))
  426. else:
  427. self.logger.info("Bulk erase failed with errors:")
  428. self.logger.debug("Erase results = %s" % erase_results)
  429. self.logger.info("Attempting individual erase for each package.")
  430. pkgspec_list = []
  431. for pkg in packages:
  432. pkg_modified = False
  433. for inst in pkg:
  434. if pkg.get('name') != 'gpg-pubkey':
  435. pkgspec = { 'name':pkg.get('name'),
  436. 'epoch':inst.get('epoch', None),
  437. 'version':inst.get('version'),
  438. 'release':inst.get('release'),
  439. 'arch':inst.get('arch') }
  440. pkgspec_list.append(pkgspec)
  441. else:
  442. pkgspec = { 'name':pkg.get('name'),
  443. 'version':inst.get('version'),
  444. 'release':inst.get('release')}
  445. self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
  446. % (pkgspec.get('name'), self.str_evra(pkgspec)))
  447. self.logger.info(" This package will be deleted in a future version of the RPMng driver.")
  448. continue # Don't delete the gpg-pubkey packages for now.
  449. erase_results = rpmtools.rpm_erase([pkgspec], self.erase_flags)
  450. if erase_results == []:
  451. pkg_modified = True
  452. self.logger.info("Deleted %s %s" % \
  453. (pkgspec.get('name'), self.str_evra(pkgspec)))
  454. else:
  455. self.logger.error("unable to delete %s %s" % \
  456. (pkgspec.get('name'), self.str_evra(pkgspec)))
  457. self.logger.debug("Failure = %s" % erase_results)
  458. if pkg_modified == True:
  459. self.modified.append(pkg)
  460. self.RefreshPackages()
  461. self.extra = self.FindExtraPackages()
  462. def FixInstance(self, instance, inst_status):
  463. """"
  464. Control if a reinstall of a package happens or not based on the
  465. results from RPMng.VerifyPackage().
  466. Return True to reinstall, False to not reintstall.
  467. """
  468. fix = False
  469. if inst_status.get('installed', False) == False:
  470. if instance.get('installed_action', 'install') == "install" and \
  471. self.installed_action == "install":
  472. fix = True
  473. else:
  474. self.logger.debug('Installed Action for %s %s is to not install' % \
  475. (inst_status.get('pkg').get('name'),
  476. self.str_evra(instance)))
  477. elif inst_status.get('version_fail', False) == True:
  478. if instance.get('version_fail_action', 'upgrade') == "upgrade" and \
  479. self.version_fail_action == "upgrade":
  480. fix = True
  481. else:
  482. self.logger.debug('Version Fail Action for %s %s is to not upgrade' % \
  483. (inst_status.get('pkg').get('name'),
  484. self.str_evra(instance)))
  485. elif inst_status.get('verify_fail', False) == True and self.name == "RPMng":
  486. # yum can't reinstall packages so only do this for rpm.
  487. if instance.get('verify_fail_action', 'reinstall') == "reinstall" and \
  488. self.verify_fail_action == "reinstall":
  489. for inst in inst_status.get('verify'):
  490. # This needs to be a for loop rather than a straight get()
  491. # because the underlying routines handle multiple packages
  492. # and return a list of results.
  493. self.logger.debug('reinstall_check: %s %s:%s-%s.%s' % inst.get('nevra'))
  494. if inst.get("hdr", False):
  495. fix = True
  496. elif inst.get('files', False):
  497. # Parse rpm verify file results
  498. for file_result in inst.get('files', []):
  499. self.logger.debug('reinstall_check: file: %s' % file_result)
  500. if file_result[-2] != 'c':
  501. fix = True
  502. break
  503. # Shouldn't really need this, but included for clarity.
  504. elif inst.get("deps", False):
  505. fix = False
  506. else:
  507. self.logger.debug('Verify Fail Action for %s %s is to not reinstall' % \
  508. (inst_status.get('pkg').get('name'),
  509. self.str_evra(instance)))
  510. return fix
  511. def Install(self, packages, states):
  512. """
  513. Try and fix everything that RPMng.VerifyPackages() found wrong for
  514. each Package Entry. This can result in individual RPMs being
  515. installed (for the first time), reinstalled, deleted, downgraded
  516. or upgraded.
  517. packages is a list of Package Elements that has
  518. states[<Package Element>] == False
  519. The following effects occur:
  520. - states{} is conditionally updated for each package.
  521. - self.installed{} is rebuilt, possibly multiple times.
  522. - self.instance_statusi{} is conditionally updated for each instance
  523. of a package.
  524. - Each package will be added to self.modified[] if its states{}
  525. entry is set to True.
  526. """
  527. self.logger.info('Runing RPMng.Install()')
  528. install_only_pkgs = []
  529. gpg_keys = []
  530. upgrade_pkgs = []
  531. # Remove extra instances.
  532. # Can not reverify because we don't have a package entry.
  533. if len(self.extra_instances) > 0:
  534. if (self.setup.get('remove') == 'all' or \
  535. self.setup.get('remove') == 'packages') and\
  536. not self.setup.get('dryrun'):
  537. self.RemovePackages(self.extra_instances)
  538. else:
  539. self.logger.info("The following extra package instances will be removed by the '-r' option:")
  540. for pkg in self.extra_instances:
  541. for inst in pkg:
  542. self.logger.info(" %s %s" % (pkg.get('name'), self.str_evra(inst)))
  543. # Figure out which instances of the packages actually need something
  544. # doing to them and place in the appropriate work 'queue'.
  545. for pkg in packages:
  546. for inst in [instn for instn in pkg if instn.tag \
  547. in ['Instance', 'Package']]:
  548. if self.FixInstance(inst, self.instance_status[inst]):
  549. if pkg.get('name') == 'gpg-pubkey':
  550. gpg_keys.append(inst)
  551. elif pkg.get('name') in self.installOnlyPkgs:
  552. install_only_pkgs.append(inst)
  553. else:
  554. upgrade_pkgs.append(inst)
  555. # Fix installOnlyPackages
  556. if len(install_only_pkgs) > 0:
  557. self.logger.info("Attempting to install 'install only packages'")
  558. install_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
  559. inst.get('simplefile')) \
  560. for inst in install_only_pkgs])
  561. self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args)
  562. cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \
  563. install_args)
  564. if cmdrc == 0:
  565. # The rpm command succeeded. All packages installed.
  566. self.logger.info("Single Pass for InstallOnlyPkgs Succeded")
  567. self.RefreshPackages()
  568. else:
  569. # The rpm command failed. No packages installed.
  570. # Try installing instances individually.
  571. self.logger.error("Single Pass for InstallOnlyPackages Failed")
  572. installed_instances = []
  573. for inst in install_only_pkgs:
  574. install_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
  575. inst.get('simplefile'))
  576. self.logger.debug("rpm --install --quiet --oldpackage %s" % install_args)
  577. cmdrc, output = self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs %s" % \
  578. install_args)
  579. if cmdrc == 0:
  580. installed_instances.append(inst)
  581. else:
  582. self.logger.debug("InstallOnlyPackage %s %s would not install." % \
  583. (self.instance_status[inst].get('pkg').get('name'), \
  584. self.str_evra(inst)))
  585. install_pkg_set = set([self.instance_status[inst].get('pkg') \
  586. for inst in install_only_pkgs])
  587. self.RefreshPackages()
  588. # Install GPG keys.
  589. if len(gpg_keys) > 0:
  590. for inst in gpg_keys:
  591. self.logger.info("Installing GPG keys.")
  592. key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
  593. inst.get('simplefile'))
  594. cmdrc, output = self.cmd.run("rpm --import %s" % key_arg)
  595. if cmdrc != 0:
  596. self.logger.debug("Unable to install %s-%s" % \
  597. (self.instance_status[inst].get('pkg').get('name'), \
  598. self.str_evra(inst)))
  599. else:
  600. self.logger.debug("Installed %s-%s-%s" % \
  601. (self.instance_status[inst].get('pkg').get('name'), \
  602. inst.get('version'), inst.get('release')))
  603. self.RefreshPackages()
  604. self.gpg_keyids = self.getinstalledgpg()
  605. pkg = self.instance_status[gpg_keys[0]].get('pkg')
  606. states[pkg] = self.VerifyPackage(pkg, [])
  607. # Fix upgradeable packages.
  608. if len(upgrade_pkgs) > 0:
  609. self.logger.info("Attempting to upgrade packages")
  610. upgrade_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
  611. inst.get('simplefile')) \
  612. for inst in upgrade_pkgs])
  613. cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \
  614. upgrade_args)
  615. if cmdrc == 0:
  616. # The rpm command succeeded. All packages upgraded.
  617. self.logger.info("Single Pass for Upgraded Packages Succeded")
  618. upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \
  619. for inst in upgrade_pkgs])
  620. self.RefreshPackages()
  621. else:
  622. # The rpm command failed. No packages upgraded.
  623. # Try upgrading instances individually.
  624. self.logger.error("Single Pass for Upgrading Packages Failed")
  625. upgraded_instances = []
  626. for inst in upgrade_pkgs:
  627. upgrade_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
  628. inst.get('simplefile'))
  629. #self.logger.debug("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \
  630. # upgrade_args)
  631. cmdrc, output = self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % upgrade_args)
  632. if cmdrc == 0:
  633. upgraded_instances.append(inst)
  634. else:
  635. self.logger.debug("Package %s %s would not upgrade." % \
  636. (self.instance_status[inst].get('pkg').get('name'), \
  637. self.str_evra(inst)))
  638. upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \
  639. for inst in upgrade_pkgs])
  640. self.RefreshPackages()
  641. if not self.setup['kevlar']:
  642. for pkg_entry in packages:
  643. self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name')))
  644. states[pkg_entry] = self.VerifyPackage(pkg_entry, \
  645. self.modlists.get(pkg_entry, []))
  646. for entry in [ent for ent in packages if states[ent]]:
  647. self.modified.append(entry)
  648. def canInstall(self, entry):
  649. """Test if entry has enough information to be installed."""
  650. if not self.handlesEntry(entry):
  651. return False
  652. if 'failure' in entry.attrib:
  653. self.logger.error("Cannot install entry %s:%s with bind failure" % \
  654. (entry.tag, entry.get('name')))
  655. return False
  656. instances = entry.findall('Instance')
  657. # If the entry wasn't verifiable, then we really don't want to try and fix something
  658. # that we don't know is broken.
  659. if not self.canVerify(entry):
  660. self.logger.debug("WARNING: Package %s was not verifiable, not passing to Install()" \
  661. % entry.get('name'))
  662. return False
  663. if not instances:
  664. # Old non Instance format, unmodified.
  665. if entry.get('name') == 'gpg-pubkey':
  666. # gpg-pubkey packages aren't really pacakges, so we have to do
  667. # something a little different.
  668. # Check that the Package Level has what we need for verification.
  669. if [attr for attr in self.__gpg_ireq__[entry.tag] if attr not in entry.attrib]:
  670. self.logger.error("Incomplete information for entry %s:%s; cannot install" \
  671. % (entry.tag, entry.get('name')))
  672. return False
  673. else:
  674. if [attr for attr in self.__ireq__[entry.tag] if attr not in entry.attrib]:
  675. self.logger.error("Incomplete information for entry %s:%s; cannot install" \
  676. % (entry.tag, entry.get('name')))
  677. return False
  678. else:
  679. if entry.get('name') == 'gpg-pubkey':
  680. # gpg-pubkey packages aren't really pacakges, so we have to do
  681. # something a little different.
  682. # Check that the Package Level has what we need for verification.
  683. if [attr for attr in self.__new_gpg_ireq__[entry.tag] if attr not in entry.attrib]:
  684. self.logger.error("Incomplete information for entry %s:%s; cannot install" \
  685. % (entry.tag, entry.get('name')))
  686. return False
  687. # Check that the Instance Level has what we need for verification.
  688. for inst in instances:
  689. if [attr for attr in self.__new_gpg_ireq__[inst.tag] \
  690. if attr not in inst.attrib]:
  691. self.logger.error("Incomplete information for entry %s:%s; cannot install"\
  692. % (inst.tag, entry.get('name')))
  693. return False
  694. else:
  695. # New format with Instances.
  696. # Check that the Package Level has what we need for verification.
  697. if [attr for attr in self.__new_ireq__[entry.tag] if attr not in entry.attrib]:
  698. self.logger.error("Incomplete information for entry %s:%s; cannot install" \
  699. % (entry.tag, entry.get('name')))
  700. self.logger.error(" Required attributes that may not be present are %s" \
  701. % (self.__new_ireq__[entry.tag]))
  702. return False
  703. # Check that the Instance Level has what we need for verification.
  704. for inst in instances:
  705. if inst.tag == 'Instance':
  706. if [attr for attr in self.__new_ireq__[inst.tag] \
  707. if attr not in inst.attrib]:
  708. self.logger.error("Incomplete information for %s of package %s; cannot install" \
  709. % (inst.tag, entry.get('name')))
  710. self.logger.error(" Required attributes that may not be present are %s" \
  711. % (self.__new_ireq__[inst.tag]))
  712. return False
  713. return True
  714. def canVerify(self, entry):
  715. """
  716. Test if entry has enough information to be verified.
  717. Three types of entries are checked.
  718. Old style Package
  719. New style Package with Instances
  720. pgp-pubkey packages
  721. Also the old style entries get modified after the first
  722. VerifyPackage() run, so there needs to be a second test.
  723. """
  724. if not self.handlesEntry(entry):
  725. return False
  726. if 'failure' in entry.attrib:
  727. self.logger.error("Entry %s:%s reports bind failure: %s" % \
  728. (entry.tag, entry.get('name'), entry.get('failure')))
  729. return False
  730. # We don't want to do any checks so we don't care what the entry has in it.
  731. if self.pkg_checks == 'false' or \
  732. entry.get('pkg_checks', 'true').lower() == 'false':
  733. return True
  734. instances = entry.findall('Instance')
  735. if not instances:
  736. # Old non Instance format, unmodified.
  737. if entry.get('name') == 'gpg-pubkey':
  738. # gpg-pubkey packages aren't really pacakges, so we have to do
  739. # something a little different.
  740. # Check that the Package Level has what we need for verification.
  741. if [attr for attr in self.__gpg_req__[entry.tag] if attr not in entry.attrib]:
  742. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  743. % (entry.tag, entry.get('name')))
  744. return False
  745. elif entry.tag == 'Path' and entry.get('type') == 'ignore':
  746. # ignored Paths are only relevant during failed package
  747. # verification
  748. pass
  749. else:
  750. if [attr for attr in self.__req__[entry.tag] if attr not in entry.attrib]:
  751. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  752. % (entry.tag, entry.get('name')))
  753. return False
  754. else:
  755. if entry.get('name') == 'gpg-pubkey':
  756. # gpg-pubkey packages aren't really pacakges, so we have to do
  757. # something a little different.
  758. # Check that the Package Level has what we need for verification.
  759. if [attr for attr in self.__new_gpg_req__[entry.tag] if attr not in entry.attrib]:
  760. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  761. % (entry.tag, entry.get('name')))
  762. return False
  763. # Check that the Instance Level has what we need for verification.
  764. for inst in instances:
  765. if [attr for attr in self.__new_gpg_req__[inst.tag] \
  766. if attr not in inst.attrib]:
  767. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  768. % (inst.tag, inst.get('name')))
  769. return False
  770. else:
  771. # New format with Instances, or old style modified.
  772. # Check that the Package Level has what we need for verification.
  773. if [attr for attr in self.__new_req__[entry.tag] if attr not in entry.attrib]:
  774. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  775. % (entry.tag, entry.get('name')))
  776. return False
  777. # Check that the Instance Level has what we need for verification.
  778. for inst in instances:
  779. if inst.tag == 'Instance':
  780. if [attr for attr in self.__new_req__[inst.tag] \
  781. if attr not in inst.attrib]:
  782. self.logger.error("Incomplete information for entry %s:%s; cannot verify" \
  783. % (inst.tag, inst.get('name')))
  784. return False
  785. return True
  786. def FindExtraPackages(self):
  787. """Find extra packages."""
  788. packages = [entry.get('name') for entry in self.getSupportedEntries()]
  789. extras = []
  790. for (name, instances) in list(self.installed.items()):
  791. if name not in packages:
  792. extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype)
  793. for installed_inst in instances:
  794. if self.setup['extra']:
  795. self.logger.info("Extra Package %s %s." % \
  796. (name, self.str_evra(installed_inst)))
  797. tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \
  798. version = installed_inst.get('version'), \
  799. release = installed_inst.get('release'))
  800. if installed_inst.get('epoch', None) != None:
  801. tmp_entry.set('epoch', str(installed_inst.get('epoch')))
  802. if installed_inst.get('arch', None) != None:
  803. tmp_entry.set('arch', installed_inst.get('arch'))
  804. extras.append(extra_entry)
  805. return extras
  806. def FindExtraInstances(self, pkg_entry, installed_entry):
  807. """
  808. Check for installed instances that are not in the config.
  809. Return a Package Entry with Instances to remove, or None if there
  810. are no Instances to remove.
  811. """
  812. name = pkg_entry.get('name')
  813. extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype)
  814. instances = [inst for inst in pkg_entry if inst.tag == 'Instance' or inst.tag == 'Package']
  815. if name in self.installOnlyPkgs:
  816. for installed_inst in installed_entry:
  817. not_found = True
  818. for inst in instances:
  819. if self.pkg_vr_equal(inst, installed_inst) or \
  820. self.inst_evra_equal(inst, installed_inst):
  821. not_found = False
  822. break
  823. if not_found == True:
  824. # Extra package.
  825. self.logger.info("Extra InstallOnlyPackage %s %s." % \
  826. (name, self.str_e