Tryag File Manager
Home
-
Turbo Force
Current Path :
/
usr
/
share
/
rhsm
/
subscription_manager
/
Upload File :
New :
File
Dir
//usr/share/rhsm/subscription_manager/certlib.py
# # Copyright (c) 2010 Red Hat, Inc. # # Authors: Jeff Ortel <jortel@redhat.com> # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # from datetime import datetime, timedelta import gettext import logging import syslog import socket from rhsm.config import initConfig from rhsm.certificate import Key, create_from_pem, GMT from subscription_manager.certdirectory import Writer from subscription_manager import rhelentbranding from subscription_manager.identity import ConsumerIdentity from subscription_manager.injection import CERT_SORTER, PLUGIN_MANAGER, require import subscription_manager.injection as inj from subscription_manager.lock import Lock log = logging.getLogger('rhsm-app.' + __name__) _ = gettext.gettext cfg = initConfig() def system_log(message, priority=syslog.LOG_NOTICE): syslog.openlog("subscription-manager") syslog.syslog(priority, message.encode("utf-8")) class ActionLock(Lock): PATH = '/var/run/rhsm/cert.pid' def __init__(self): Lock.__init__(self, self.PATH) class DataLib(object): def __init__(self, lock=None, uep=None): self.lock = lock # Need to do this here rather than with a default argument to prevent # circular dependency problems: if not self.lock: self.lock = ActionLock() self.uep = uep def update(self): self.lock.acquire() try: return self._do_update() finally: self.lock.release() def _do_update(self): return class CertLib(DataLib): def __init__(self, lock=ActionLock(), uep=None): DataLib.__init__(self, lock, uep) def delete(self, serial_numbers): lock = self.lock lock.acquire() try: return self._do_delete(serial_numbers) finally: lock.release() def _do_update(self): action = UpdateAction(uep=self.uep) return action.perform(lock=self.lock) def _do_delete(self, serial_numbers): action = DeleteAction() return action.perform(serial_numbers) class HealingLib(DataLib): """ An object used to run healing nightly. Checks cert validity for today, heals if necessary, then checks for 24 hours from now, so we theoretically will never have invalid certificats if subscriptions are available. """ def __init__(self, lock=ActionLock(), uep=None, product_dir=None): DataLib.__init__(self, lock, uep) self._product_dir = product_dir or inj.require(inj.PROD_DIR) self.plugin_manager = require(PLUGIN_MANAGER) def _do_update(self): uuid = ConsumerIdentity.read().getConsumerId() consumer = self.uep.getConsumer(uuid) if 'autoheal' in consumer and consumer['autoheal']: try: log.info("Checking if system requires healing.") today = datetime.now(GMT()) tomorrow = today + timedelta(days=1) # Check if we're invalid today and heal if so. If we are # valid, see if 24h from now is greater than our "valid until" # date, and heal for tomorrow if so. cs = require(CERT_SORTER) cert_updater = CertLib(lock=self.lock, uep=self.uep) if not cs.is_valid(): log.warn("Found invalid entitlements for today: %s" % today) self.plugin_manager.run("pre_auto_attach", consumer_uuid=uuid) ents = self.uep.bind(uuid, today) self.plugin_manager.run("post_auto_attach", consumer_uuid=uuid, entitlement_data=ents) cert_updater.update() else: log.info("Entitlements are valid for today: %s" % today) if cs.compliant_until is None: # Edge case here, not even sure this can happen as we # should have a compliant until date if we're valid # today, but just in case: log.warn("Got valid status from server but no valid until date.") elif tomorrow > cs.compliant_until: log.warn("Entitlements will be invalid by tomorrow: %s" % tomorrow) self.plugin_manager.run("pre_auto_attach", consumer_uuid=uuid) ents = self.uep.bind(uuid, tomorrow) self.plugin_manager.run("post_auto_attach", consumer_uuid=uuid, entitlement_data=ents) cert_updater.update() else: log.info("Entitlements are valid for tomorrow: %s" % tomorrow) except Exception, e: log.error("Error attempting to auto-heal:") log.exception(e) return 0 else: log.info("Auto-heal check complete.") return 1 else: log.info("Auto-heal disabled on server, skipping.") return 0 class IdentityCertLib(DataLib): """ An object to update the identity certificate in the event the server deems it is about to expire. This is done to prevent the identity certificate from expiring thus disallowing connection to the server for updates. """ def __init__(self, lock=ActionLock(), uep=None): super(IdentityCertLib, self).__init__(lock, uep) def _do_update(self): if not ConsumerIdentity.existsAndValid(): # we could in theory try to update the id in the # case of it being bogus/corrupted, ala #844069, # but that seems unneeded return 0 from subscription_manager import managerlib idcert = ConsumerIdentity.read() uuid = idcert.getConsumerId() consumer = self.uep.getConsumer(uuid) # only write the cert if the serial has changed if idcert.getSerialNumber() != consumer['idCert']['serial']['serial']: log.debug('identity certificate changed, writing new one') managerlib.persist_consumer_cert(consumer) return 1 class Action: def __init__(self, uep=None, entdir=None): self.entdir = entdir or inj.require(inj.ENT_DIR) self.uep = uep def build(self, bundle): keypem = bundle['key'] crtpem = bundle['cert'] key = Key(keypem) cert = create_from_pem(crtpem) return (key, cert) class DeleteAction(Action): def perform(self, serial_numbers): for sn in serial_numbers: cert = self.entdir.find(sn) if cert is None: continue cert.delete() return self class UpdateAction(Action): def __init__(self, uep=None, entdir=None): Action.__init__(self, uep=uep, entdir=entdir) def perform(self, lock=None): report = UpdateReport() local = self._get_local_serials(report) try: expected = self._get_expected_serials(report) except socket.error, ex: log.exception(ex) log.error('Cannot modify subscriptions while disconnected') raise Disconnected() missing_serials = self._find_missing_serials(local, expected) rogue_serials = self._find_rogue_serials(local, expected) self.delete(rogue_serials, report) exceptions = self.install(missing_serials, report) log.info('certs updated:\n%s', report) self.syslog_results(report) if missing_serials or rogue_serials: # refresh yum repo's now self.repo_hook(lock=lock) # NOTE: Since we have the yum repos defined here now # we could update product id certs here, or install # them if they are needed, but missing. That way the # branding installs could be more accurate. # reload certs and update branding self.branding_hook() # WARNING: TODO: XXX: this is returning a tuple, the parent class and # all other sub-classes return an int, which somewhat defeats # the purpose... return (report.updates(), exceptions) def _find_missing_serials(self, local, expected): """ Find serials from the server we do not have locally. """ missing = [sn for sn in expected if sn not in local] return missing def _find_rogue_serials(self, local, expected): """ Find serials we have locally but are not on the server. """ rogue = [local[sn] for sn in local if not sn in expected] return rogue def syslog_results(self, report): for cert in report.added: system_log("Added subscription for '%s' contract '%s'" % (cert.order.name, cert.order.contract)) for product in cert.products: system_log("Added subscription for product '%s'" % (product.name)) for cert in report.rogue: system_log("Removed subscription for '%s' contract '%s'" % (cert.order.name, cert.order.contract)) for product in cert.products: system_log("Removed subscription for product '%s'" % (product.name)) def _get_local_serials(self, report): local = {} #certificates in grace period were being renamed everytime. #this makes sure we don't try to re-write certificates in #grace period # XXX since we don't use grace period, this might not be needed self.entdir.refresh() for valid in self.entdir.list(): sn = valid.serial report.valid.append(sn) local[sn] = valid return local def _get_consumer_id(self): try: cid = ConsumerIdentity.read() return cid.getConsumerId() except Exception, e: log.error(e) raise Disconnected() def get_certificate_serials_list(self): results = [] # if there is no UEP object, short circuit if self.uep is None: return results reply = self.uep.getCertificateSerials(self._get_consumer_id()) for d in reply: sn = d['serial'] results.append(sn) return results def _get_expected_serials(self, report): exp = self.get_certificate_serials_list() report.expected = exp return exp def delete(self, rogue, report): for cert in rogue: try: cert.delete() report.rogue.append(cert) except OSError, er: log.exception(er) log.warn("Failed to delete cert") # If we just deleted certs, we need to refresh the now stale # entitlement directory before we go to delete expired certs. rogue_count = len(report.rogue) if rogue_count > 0: print gettext.ngettext("%s local certificate has been deleted.", "%s local certificates have been deleted.", rogue_count) % rogue_count self.entdir.refresh() def get_certificates_by_serial_list(self, sn_list): result = [] if sn_list: sn_list = [str(sn) for sn in sn_list] reply = self.uep.getCertificates(self._get_consumer_id(), serials=sn_list) for cert in reply: result.append(cert) return result def repo_hook(self, lock): """Update yum repos Args: lock: an ActionLock, in this case certlib.UpdateAction lock, since repo_kook is called when UpdateAction has already acquired a lock. """ try: # repolib/RepoLib imports certlib, so import late until # we factor out our circular deps from subscription_manager.repolib import RepoLib rl = RepoLib(lock=lock, uep=self.uep) rl.update() except Exception, e: log.debug(e) log.debug("Failed to update repos") def branding_hook(self): """Update branding info based on entitlement cert changes""" # RHELBrandsInstaller will use latest ent_dir contents brands_installer = rhelentbranding.RHELBrandsInstaller() brands_installer.install() def install(self, serials, report): br = Writer() exceptions = [] for bundle in self.get_certificates_by_serial_list(serials): try: key, cert = self.build(bundle) br.write(key, cert) report.added.append(cert) except Exception, e: log.exception(e) log.error( 'Bundle not loaded:\n%s\n%s', bundle, e) exceptions.append(e) return exceptions class Disconnected(Exception): pass class UpdateReport: def __init__(self): self.valid = [] self.expected = [] self.added = [] self.rogue = [] def updates(self): return (len(self.added) + len(self.rogue)) def write(self, s, title, certificates): indent = ' ' s.append(title) if certificates: for c in certificates: products = c.products if not products: s.append('%s[sn:%d (%s) @ %s]' % (indent, c.serial, c.order.name, c.path)) for product in products: s.append('%s[sn:%d (%s,) @ %s]' % (indent, c.serial, product.name, c.path)) else: s.append('%s<NONE>' % indent) def __str__(self): s = [] s.append(_('Total updates: %d') % self.updates()) s.append(_('Found (local) serial# %s') % self.valid) s.append(_('Expected (UEP) serial# %s') % self.expected) self.write(s, _('Added (new)'), self.added) self.write(s, _('Deleted (rogue):'), self.rogue) return '\n'.join(s) def main(): print _('Updating entitlement certificates') certlib = CertLib() updates = certlib.update() print _('%d updates required') % updates print _('done') if __name__ == '__main__': main()