示例#1
0
    def __init__(self, options):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = config.Config(initConfig())

        # Sometimes we need to send up the entire contents of the system id file
        # which is referred to in Satellite 5 nomenclature as a "certificate"
        # although it is not an X509 certificate.
        try:
            self.system_id_contents = open(self.rhncfg["systemIdPath"],
                                           'r').read()
        except IOError:
            system_exit(
                os.EX_IOERR,
                _("Could not read legacy system id at %s") %
                self.rhncfg["systemIdPath"])

        self.system_id = self.get_system_id(self.system_id_contents)

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.consumer_id = None

        self.options = options
        self.is_hosted = is_hosted()
    def doCommand(self, base, basecmd, extcmds):
        opts = base.plugins.cmdline[0]
        pkgs = base.rpmdb
        product_dir = ProductDirectory()
        product = {}
        for p in product_dir.list():
            for p in p.products:
                product[p.id] = p.name

        product_db = ProductDatabase()
        product_db.read()
        # convert IDs to names in the mapping
        product_repo_mapping = dict((product[k], v) for k, v in product_db.content.iteritems())

        for product in extcmds:
            for ipkg in sorted(pkgs):
                if "from_repo" in ipkg.yumdb_info and ipkg.yumdb_info.from_repo in product_repo_mapping.get(product):
                    # add the package to the erasure transaction
                    base.remove(ipkg)

        if len(base.tsInfo) == 0:
            return 0, ["No packages found for selected products"]

        if base.doTransaction() == 0:
            return 0, ["Removed packages for selected products"]
        else:
            return 0, ["Error occured while removing packages. Please see yum.log for more details."]
示例#3
0
    def __init__(self, options):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = config.Config(initConfig())

        # Sometimes we need to send up the entire contents of the system id file
        # which is referred to in Satellite 5 nomenclature as a "certificate"
        # although it is not an X509 certificate.
        try:
            self.system_id_contents = open(self.rhncfg["systemIdPath"], 'r').read()
        except IOError:
            system_exit(os.EX_IOERR, _("Could not read legacy system id at %s") % self.rhncfg["systemIdPath"])

        self.system_id = self.get_system_id(self.system_id_contents)

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.consumer_id = None

        self.options = options
        self.is_hosted = is_hosted()
示例#4
0
    def __init__(self):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = rhsm.config.initConfig()

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.parser = OptionParser(usage=USAGE,
                                   formatter=WrappedIndentedHelpFormatter())
        self.add_parser_options()
示例#5
0
    def __init__(self, options):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = rhsm.config.initConfig()

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.consumer_id = None

        self.options = options
        self.is_hosted = is_hosted()
示例#6
0
 def test_product_database(self):
     Path.ROOT = '/mnt/sysimage'
     prod_db = ProductDatabase()
     self.assertEquals('/mnt/sysimage/var/lib/rhsm/productid.js',
             prod_db.dir.abspath('productid.js'))
示例#7
0
class MigrationEngine(object):
    def __init__(self, options):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = config.Config(initConfig())

        # Sometimes we need to send up the entire contents of the system id file
        # which is referred to in Satellite 5 nomenclature as a "certificate"
        # although it is not an X509 certificate.
        try:
            self.system_id_contents = open(self.rhncfg["systemIdPath"], 'r').read()
        except IOError:
            system_exit(os.EX_IOERR, _("Could not read legacy system id at %s") % self.rhncfg["systemIdPath"])

        self.system_id = self.get_system_id(self.system_id_contents)

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.consumer_id = None

        self.options = options
        self.is_hosted = is_hosted()

    def authenticate(self, username, password, user_prompt, pw_prompt):
        if not username:
            username = six.moves.input(user_prompt).strip()
            readline.clear_history()

        if not password:
            password = getpass.getpass(prompt=pw_prompt)

        return UserCredentials(username, password)

    def get_auth(self):
        if self.options.registration_state == "keep":
            self.legacy_creds = UserCredentials(None, None)
        else:
            self.legacy_creds = self.authenticate(self.options.legacy_user, self.options.legacy_password,
                _("Legacy username: "******"Legacy password: "******"keep":
            self.destination_creds = self.authenticate(self.options.destination_user, self.options.destination_password,
                _("Destination username: "******"Destination password: "******"http://":
                http_proxy = http_proxy[7:]
            try:
                self.proxy_host, self.proxy_port = http_proxy.split(':')
            except ValueError as e:
                log.exception(e)
                system_exit(os.EX_CONFIG, _("Could not read legacy proxy settings.  ") + SEE_LOG_FILE)

            if self.rhncfg['enableProxyAuth']:
                self.proxy_user = self.rhncfg['proxyUser']
                self.proxy_pass = self.rhncfg['proxyPassword']

            log.info("Using proxy %s:%s" % (self.proxy_host, self.proxy_port))
            if self.options.noproxy:
                # If the user doesn't want to use a proxy to connect to their subscription
                # management server, then remove any proxy information that may have crept in.
                self.rhsmcfg['server']['proxy_hostname'] = ''
                self.rhsmcfg['server']['proxy_port'] = ''
                self.rhsmcfg['server']['proxy_user'] = ''
                self.rhsmcfg['server']['proxy_password'] = ''
            else:
                self.rhsmcfg['server']['proxy_hostname'] = self.proxy_host
                self.rhsmcfg['server']['proxy_port'] = self.proxy_port
                self.rhsmcfg['server']['proxy_user'] = self.proxy_user or ''
                self.rhsmcfg['server']['proxy_password'] = self.proxy_pass or ''
            self.rhsmcfg.persist()

    def _get_connection_info(self):
        url_parse_error = os.EX_USAGE
        try:
            if self.options.destination_url is None:
                url_parse_error = os.EX_CONFIG
                hostname = self.rhsmcfg['server']['hostname']
                port = self.rhsmcfg['server'].get_int('port')
                prefix = self.rhsmcfg['server']['prefix']
            else:
                (_user, _password, hostname, port, prefix) = parse_url(self.options.destination_url, default_port=443)
        except ServerUrlParseError as e:
            system_exit(url_parse_error, _("Error parsing server URL: %s") % e.msg)

        connection_info = {'host': hostname, 'ssl_port': int(port), 'handler': prefix}

        if not self.options.noproxy:
            connection_info['proxy_hostname_arg'] = self.proxy_host
            connection_info['proxy_port_arg'] = self.proxy_port and int(self.proxy_port)
            connection_info['proxy_user_arg'] = self.proxy_user
            connection_info['proxy_password_arg'] = self.proxy_pass

        return connection_info

    def get_candlepin_connection(self, username, password):
        self.cp_provider = inj.require(inj.CP_PROVIDER)
        connection_info = self._get_connection_info()
        self.cp_provider.set_connection_info(**connection_info)

        if username and password:
            self.cp_provider.set_user_pass(username, password)
            return self.cp_provider.get_basic_auth_cp()

        return self.cp_provider.get_no_auth_cp()

    def check_ok_to_proceed(self):
        # check if this machine is already registered to Certicate-based RHN
        identity = inj.require(inj.IDENTITY)
        if identity.is_valid():
            if self.options.five_to_six:
                msgs = [_("This system appears to already be registered to Satellite 6.")]
            else:
                msgs = [_("This system appears to already be registered to Red Hat Subscription Management.")]
                msgs.append(_("Please visit https://access.redhat.com/management/consumers/%s to view the profile details.") % identity.uuid)
            system_exit(1, msgs)

        try:
            self.cp.getStatus()
        except ssl.SSLError as e:
            print(_("The CA certificate for the destination server has not been installed."))
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

    def get_org(self, username):
        try:
            owner_list = self.cp.getOwnerList(username)
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

        if len(owner_list) == 0:
            system_exit(1, _("%s cannot register with any organizations.") % username)
        else:
            if self.options.org:
                org_input = self.options.org
            elif len(owner_list) == 1:
                org_input = owner_list[0]['key']
            else:
                org_input = six.moves.input(_("Org: ")).strip()
                readline.clear_history()

            org = None
            for owner_data in owner_list:
                if owner_data['key'] == org_input or owner_data['displayName'] == org_input:
                    org = owner_data['key']
                    break
            if not org:
                system_exit(os.EX_DATAERR, _("Couldn't find organization '%s'.") % org_input)
        return org

    def get_environment(self, owner_key):
        environment_list = []
        try:
            if self.cp.supports_resource('environments'):
                environment_list = self.cp.getEnvironmentList(owner_key)
            elif self.options.environment:
                system_exit(os.EX_UNAVAILABLE, _("Environments are not supported by this server."))
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

        environment = None
        if len(environment_list) > 0:
            if self.options.environment:
                env_input = self.options.environment
            elif len(environment_list) == 1:
                env_input = environment_list[0]['name']
            else:
                env_input = six.moves.input(_("Environment: ")).strip()
                readline.clear_history()

            for env_data in environment_list:
                # See BZ #978001
                if (env_data['name'] == env_input or
                   ('label' in env_data and env_data['label'] == env_input) or
                   ('displayName' in env_data and env_data['displayName'] == env_input)):
                    environment = env_data['name']
                    break
            if not environment:
                system_exit(os.EX_DATAERR, _("Couldn't find environment '%s'.") % env_input)

        return environment

    def connect_to_rhn(self, credentials):
        hostname = self.rhncfg['serverURL'].split('/')[2]
        server_url = 'https://%s/rpc/api' % (hostname)

        try:
            if self.rhncfg['enableProxy']:
                proxy = "%s:%s" % (self.proxy_host, self.proxy_port)
                log.info("Using proxy %s for legacy API methods" % (proxy))
                if self.rhncfg['enableProxyAuth']:
                    proxy = "@".join(["%s:%s" % (self.proxy_user, self.proxy_pass), proxy])
            else:
                proxy = None

            rpc_session = rpclib.Server(server_url, proxy=proxy)

            ca = self.rhncfg["sslCACert"]
            rpc_session.add_trusted_cert(ca)

            if credentials.username and credentials.password:
                session_key = rpc_session.auth.login(credentials.username, credentials.password)
            else:
                session_key = None

            return (rpc_session, session_key)
        except Exception as e:
            log.exception(e)
            system_exit(1, _("Unable to authenticate to legacy server.  ") + SEE_LOG_FILE)

    def check_has_access(self, rpc_session, session_key):
        try:
            if session_key is None:
                # We should not ever be here.  This method has a guard that keeps it from being
                # called when not needed.  If we see this error, someone has made a programming
                # mistake.
                raise Exception("No session key available.  Check that XMLRPC connection is being made with credentials.")

            rpc_session.system.getDetails(session_key, self.system_id)
        except Exception as e:
            log.exception(e)
            system_exit(1, _("You do not have access to system %s.  ") % self.system_id + SEE_LOG_FILE)

    def resolve_base_channel(self, label, rpc_session, session_key):
        try:
            details = rpc_session.channel.software.getDetails(session_key, label)
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, _("Problem encountered getting the list of subscribed channels.  ") + SEE_LOG_FILE)
        if details['clone_original']:
            return self.resolve_base_channel(details['clone_original'], rpc_session, session_key)
        return details

    def get_subscribed_channels_list(self, rpc_session, session_key):
        try:
            channels = getChannels().channels()
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, _("Problem encountered getting the list of subscribed channels.  ") + SEE_LOG_FILE)
        if self.options.five_to_six:
            channels = [self.resolve_base_channel(c['label'], rpc_session, session_key) for c in channels]
        return [x['label'] for x in channels]

    def print_banner(self, msg):
        print("\n+-----------------------------------------------------+")
        print(msg)
        print("+-----------------------------------------------------+")

    def check_for_conflicting_channels(self, subscribed_channels):
        jboss_channel = False
        for channel in subscribed_channels:
            if channel.startswith("jbappplatform"):
                if jboss_channel:
                    system_exit(1, _("You are subscribed to more than one jbappplatform channel."
                                    "  This script does not support that configuration."))
                jboss_channel = True

    def get_release(self):
        f = open('/etc/redhat-release')
        lines = f.readlines()
        f.close()
        release = "RHEL-" + str(lines).split(' ')[6].split('.')[0]
        return release

    def read_channel_cert_mapping(self, mappingfile):
        f = open(mappingfile)
        lines = f.readlines()
        dic_data = {}
        for line in lines:
            # Lines should be of the format
            # key: value
            # Lines beginning with non-letters will be ignored
            if re.match("^[a-zA-Z]", line):
                line = line.replace("\n", "")
                key, val = line.strip().split(": ")
                dic_data[key] = val
        return dic_data

    def handle_collisions(self, applicable_certs):
        # if we have the same product IDs mapping to multiple certificates, we must abort.
        collisions = dict((prod_id, mappings) for prod_id, mappings in list(applicable_certs.items()) if len(mappings) > 1)
        if not collisions:
            return

        log.error("Aborting. Detected the following product ID collisions: %s", collisions)
        self.print_banner(_("Unable to continue migration!"))
        print(_("You are subscribed to channels that have conflicting product certificates."))
        for prod_id, mappings in list(collisions.items()):
            # Flatten the list of lists
            colliding_channels = [item for sublist in list(mappings.values()) for item in sublist]
            print(_("The following channels map to product ID %s:") % prod_id)
            for c in sorted(colliding_channels):
                print("\t%s" % c)
        print(_("Reduce the number of channels per product ID to 1 and run migration again."))
        print(_("To remove a channel, use 'rhn-channel --remove --channel=<conflicting_channel>'."))
        sys.exit(1)

    def deploy_prod_certificates(self, subscribed_channels):
        release = self.get_release()
        mappingfile = "/usr/share/rhsm/product/" + release + "/channel-cert-mapping.txt"
        log.info("Using mapping file %s", mappingfile)

        try:
            dic_data = self.read_channel_cert_mapping(mappingfile)
        except IOError as e:
            log.exception(e)
            system_exit(os.EX_CONFIG, _("Unable to read mapping file: %(mappingfile)s.\n"
                "Please check that you have the %(package)s package installed.") % {
                    "mappingfile": mappingfile,
                    "package": "subscription-manager-migration-data"})

        applicable_certs = {}
        valid_rhsm_channels = []
        invalid_rhsm_channels = []
        unrecognized_channels = []

        for channel in subscribed_channels:
            try:
                if dic_data[channel] != 'none':
                    valid_rhsm_channels.append(channel)
                    cert = dic_data[channel]
                    log.info("Mapping found for: %s = %s", channel, cert)
                    prod_id = cert.split('-')[-1].split('.pem')[0]
                    cert_to_channels = applicable_certs.setdefault(prod_id, {})
                    cert_to_channels.setdefault(cert, []).append(channel)
                else:
                    invalid_rhsm_channels.append(channel)
                    log.info("%s is not mapped to any certificates", channel)
            except Exception:
                unrecognized_channels.append(channel)

        if invalid_rhsm_channels:
            self.print_banner(_("Channels not available on %s:") % self.options.destination_url)
            for i in invalid_rhsm_channels:
                print(i)

        if unrecognized_channels:
            self.print_banner(_("No product certificates are mapped to these legacy channels:"))
            for i in unrecognized_channels:
                print(i)

        if unrecognized_channels or invalid_rhsm_channels:
            if not self.options.force:
                system_exit(1, _("\nUse --force to ignore these channels and continue the migration.\n"))

        # At this point applicable_certs looks something like this
        # { '1': { 'cert-a-1.pem': ['channel1', 'channel2'], 'cert-b-1.pem': ['channel3'] } }
        # This is telling us that product ID 1 maps to two certificates, cert-a and cert-b.
        # Two channels map to the cert-a certificate and one channel maps to cert-b.
        # If we wind up in a situation where a user has channels that map to two different
        # certificates with the same product ID, (e.g. len(hash[product_id]) > 1) we've got a
        # collision and must abort.
        self.handle_collisions(applicable_certs)

        log.info("Certs to be installed: %s", applicable_certs)

        self.print_banner(_("Installing product certificates for these legacy channels:"))
        for i in valid_rhsm_channels:
            print(i)

        release = self.get_release()

        # creates the product directory if it doesn't already exist
        product_dir = inj.require(inj.PROD_DIR)
        db_modified = False
        for cert_to_channels in list(applicable_certs.values()):
            # At this point handle_collisions should have verified that len(cert_to_channels) == 1
            cert, channels = list(cert_to_channels.items())[0]
            source_path = os.path.join("/usr/share/rhsm/product", release, cert)
            truncated_cert_name = cert.split('-')[-1]
            destination_path = os.path.join(product_dir.path, truncated_cert_name)
            log.info("Copying %s to %s ", source_path, destination_path)
            shutil.copy2(source_path, destination_path)

            # See BZ #972883. Add an entry to the repo db telling subscription-manager
            # that packages for this product were installed by the RHN repo which
            # conveniently uses the channel name for the repo name.
            db_id = truncated_cert_name.split('.pem')[0]
            for chan in channels:
                self.db.add(db_id, chan)
                db_modified = True

        if db_modified:
            self.db.write()
        print(_("\nProduct certificates installed successfully to %s.") % product_dir.path)

    def clean_up(self, subscribed_channels):
        # Hack to address BZ 853233
        product_dir = inj.require(inj.PROD_DIR)
        if os.path.isfile(os.path.join(product_dir.path, "68.pem")) and \
            os.path.isfile(os.path.join(product_dir.path, "71.pem")):
            try:
                os.remove(os.path.join(product_dir.path, "68.pem"))
                self.db.delete("68")
                self.db.write()
                log.info("Removed 68.pem due to existence of 71.pem")
            except OSError as e:
                log.info(e)

        # Hack to address double mapping for 180.pem and 17{6|8}.pem
        is_double_mapped = [x for x in subscribed_channels if re.match(DOUBLE_MAPPED, x)]
        is_single_mapped = [x for x in subscribed_channels if re.match(SINGLE_MAPPED, x)]

        if is_double_mapped and is_single_mapped:
            try:
                os.remove(os.path.join(product_dir.path, "180.pem"))
                self.db.delete("180")
                self.db.write()
                log.info("Removed 180.pem")
            except OSError as e:
                log.info(e)

    def get_system_id(self, content):
        p = libxml2.parseDoc(content)
        system_id = int(p.xpathEval('string(//member[* = "system_id"]/value/string)').split('-')[1])
        return system_id

    def write_migration_facts(self):
        migration_date = datetime.now().isoformat()

        if not os.path.exists(FACT_FILE):
            f = open(FACT_FILE, 'w')
            json.dump({"migration.classic_system_id": self.system_id,
                       "migration.migrated_from": self.rhncfg['serverURL'],
                       "migration.migration_date": migration_date}, f)
            f.close()

    def disable_yum_rhn_plugin(self):
        # 'Inspired by' up2date_client/rhnreg.py
        """
        Disable yum-rhn-plugin by setting enabled=0 in file
        /etc/yum/pluginconf.d/rhnplugin.conf
        Can thrown IOError exception.
        """
        log.info("Disabling rhnplugin.conf")
        f = open(YUM_PLUGIN_CONF, 'r')
        lines = f.readlines()
        f.close()
        main_section = False
        f = open(YUM_PLUGIN_CONF, 'w')
        for line in lines:
            if re.match("^\[.*]", line):
                if re.match("^\[main]", line):
                    main_section = True
                else:
                    main_section = False
            if main_section:
                line = re.sub('^(\s*)enabled\s*=.+', r'\1enabled = 0', line)
            f.write(line)
        f.close()

    def legacy_unentitle(self, rpc_session):
        try:
            rpc_session.system.unentitle(self.system_id_contents)
        except Exception as e:
            log.exception("Could not remove system entitlement on Satellite 5.", e)
            system_exit(os.EX_SOFTWARE, _("Could not remove system entitlement on legacy server.  ") + SEE_LOG_FILE)
        try:
            self.disable_yum_rhn_plugin()
        except Exception:
            pass

    def legacy_purge(self, rpc_session, session_key):
        system_id_path = self.rhncfg["systemIdPath"]

        log.info("Deleting system %s from legacy server...", self.system_id)
        try:
            result = rpc_session.system.deleteSystems(session_key, self.system_id)
        except Exception:
            log.exception("Could not delete system %s from legacy server" % self.system_id)
            # If we time out or get a network error, log it and keep going.
            shutil.move(system_id_path, system_id_path + ".save")
            print(_("Did not receive a completed unregistration message from legacy server for system %s.") % self.system_id)

            if self.is_hosted:
                print(_("Please investigate on the Customer Portal at https://access.redhat.com."))
            return

        if result:
            log.info("System %s deleted.  Removing system id file and disabling rhnplugin.conf", self.system_id)
            os.remove(system_id_path)
            try:
                self.disable_yum_rhn_plugin()
            except Exception:
                pass
            print(_("System successfully unregistered from legacy server."))
        else:
            # If the legacy server reports that deletion just failed, then quit.
            system_exit(1, _("Unable to unregister system from legacy server.  ") + SEE_LOG_FILE)

    def load_transition_data(self, rpc_session):
        try:
            transition_data = rpc_session.system.transitionDataForSystem(self.system_id_contents)
            self.consumer_id = transition_data['uuid']
        except Exception as e:
            log.exception(e)
            system_exit(1, _("Could not retrieve system migration data from legacy server.  ") + SEE_LOG_FILE)

    def consumer_exists(self, consumer_id):
        try:
            self.cp.getConsumer(consumer_id)
            return True
        except Exception as e:
            log.exception(e)
            print(_("Consumer %s doesn't exist.  Creating new consumer.") % consumer_id)
            return False

    def register(self, credentials, org, environment):
        # For registering the machine, use the CLI tool to reuse the username/password (because the GUI will prompt for them again)
        # Prepended a \n so translation can proceed without hitch
        print ("")
        print(_("Attempting to register system to destination server..."))
        cmd = ['subscription-manager', 'register']

        # Candlepin doesn't want user credentials with activation keys
        # Auto-attach and environments are also forbidden
        if self.options.activation_keys:
            for key in self.options.activation_keys:
                cmd.append('--activationkey=' + key)
        else:
            cmd.append('--username='******'--password='******'--environment=' + environment)

            if self.options.auto:
                cmd.append('--auto-attach')

        if self.options.destination_url:
            cmd.append('--serverurl=' + self.options.destination_url)

        if org:
            cmd.append('--org=' + org)

        if self.options.five_to_six:
            if self.consumer_exists(self.consumer_id):
                cmd.append('--consumerid=' + self.consumer_id)

        if self.options.service_level:
            servicelevel = self.select_service_level(org, self.options.service_level)
            cmd.append('--servicelevel=' + servicelevel)

        subprocess.call(cmd)

        identity = inj.require(inj.IDENTITY)
        identity.reload()

        if not identity.is_valid():
            system_exit(2, _("\nUnable to register.\nFor further assistance, please contact Red Hat Global Support Services."))

        print(_("System '%s' successfully registered.\n") % identity.name)
        return identity

    def select_service_level(self, org, servicelevel):
        not_supported = _("Error: The service-level command is not supported by the server.")
        try:
            levels = self.cp.getServiceLevelList(org)
        except RemoteServerException as e:
            system_exit(-1, not_supported)
        except RestlibException as e:
            if e.code == 404:
                # no need to die, just skip it
                print(not_supported)
                return None
            else:
                # server supports it but something went wrong, die.
                raise e

        # Create the sla tuple before appending the empty string to the list of
        # valid slas.
        slas = [(sla, sla) for sla in levels]
        # Display an actual message for the empty string level.
        slas.append((_("No service level preference"), ""))

        # The empty string is a valid level so append it to the list.
        levels.append("")
        if servicelevel is None or \
            servicelevel.upper() not in (level.upper() for level in levels):
            if servicelevel is not None:
                print(_("\nService level \"%s\" is not available.") % servicelevel)
            menu = Menu(slas, _("Please select a service level agreement for this system."))
            servicelevel = menu.choose()
        return servicelevel

    def enable_extra_channels(self, subscribed_channels):
        # Check if system was subscribed to extra channels like supplementary, optional, fastrack etc.
        # If so, enable them in the redhat.repo file
        extra_channels = {'supplementary': False, 'productivity': False, 'optional': False}
        for subscribedChannel in subscribed_channels:
            if 'supplementary' in subscribedChannel:
                extra_channels['supplementary'] = True
            elif 'optional' in subscribedChannel:
                extra_channels['optional'] = True
            elif 'productivity' in subscribedChannel:
                extra_channels['productivity'] = True

        if True not in list(extra_channels.values()):
            return

        # create and populate the redhat.repo file
        # use the injection cp_providers consumer auth
        repolib.RepoActionInvoker().update()

        # read in the redhat.repo file
        repofile = repolib.YumRepoFile()
        repofile.read()

        # enable any extra channels we are using and write out redhat.repo
        try:
            for rhsmChannel in repofile.sections():
                if ((extra_channels['supplementary'] and re.search('supplementary$', rhsmChannel)) or
                (extra_channels['optional'] and re.search('optional-rpms$', rhsmChannel)) or
                (extra_channels['productivity'] and re.search('productivity-rpms$', rhsmChannel))):
                    log.info("Enabling extra channel '%s'" % rhsmChannel)
                    repofile.set(rhsmChannel, 'enabled', '1')
            repofile.write()
        except Exception:
            print(_("\nCouldn't enable extra repositories."))
            command = "subscription-manager repos --help"
            print(_("Please ensure system has subscriptions attached, and see '%s' to enable additional repositories") % command)

    def is_using_systemd(self):
        release_number = int(self.get_release().partition('-')[-1])
        return release_number > 6

    def is_daemon_installed(self, daemon, using_systemd):
        has_systemd_daemon = False
        if using_systemd:
            has_systemd_daemon = subprocess.call("systemctl list-units %s.service | grep %s > /dev/null 2>&1" % (daemon, daemon), shell=True) == 0
        return has_systemd_daemon or os.path.exists("/etc/init.d/%s" % daemon)

    def is_daemon_running(self, daemon, using_systemd):
        if using_systemd:
            return subprocess.call("systemctl is-active --quiet %s" % daemon, shell=True) == 0
        else:
            return subprocess.call("service %s status > /dev/null 2>&1" % daemon, shell=True) == 0

    def handle_legacy_daemons(self, using_systemd):
        print(_("Stopping and disabling legacy services..."))
        log.info("Attempting to stop and disable legacy services: %s" % " ".join(LEGACY_DAEMONS))
        for daemon in LEGACY_DAEMONS:
            if self.is_daemon_installed(daemon, using_systemd):
                self.disable_daemon(daemon, using_systemd)
                if self.is_daemon_running(daemon, using_systemd):
                    self.stop_daemon(daemon, using_systemd)

    def stop_daemon(self, daemon, using_systemd):
        if using_systemd:
            subprocess.call(["systemctl", "stop", daemon])
        else:
            subprocess.call(["service", daemon, "stop"])

    def disable_daemon(self, daemon, using_systemd):
        if using_systemd:
            subprocess.call(["systemctl", "disable", daemon])
        else:
            subprocess.call(["chkconfig", daemon, "off"])

    def remove_legacy_packages(self):
        print(_("Removing legacy packages..."))
        log.info("Attempting to remove legacy packages: %s" % " ".join(LEGACY_PACKAGES))
        subprocess.call(["yum", "remove", "-q", "-y"] + LEGACY_PACKAGES)

    def main(self, args=None):
        self.get_auth()
        self.transfer_http_proxy_settings()
        self.cp = self.get_candlepin_connection(self.destination_creds.username, self.destination_creds.password)
        self.check_ok_to_proceed()

        (rpc_session, session_key) = self.connect_to_rhn(self.legacy_creds)
        if self.options.five_to_six:
            self.load_transition_data(rpc_session)
            org = None
            environment = None
        else:
            if self.options.activation_keys:
                environment = None
                org = self.options.org
            else:
                org = self.get_org(self.destination_creds.username)
                environment = self.get_environment(org)

        if self.options.registration_state != "keep":
            self.check_has_access(rpc_session, session_key)

        print()
        print(_("Retrieving existing legacy subscription information..."))
        subscribed_channels = self.get_subscribed_channels_list(rpc_session, session_key)
        self.print_banner(_("System is currently subscribed to these legacy channels:"))
        for channel in subscribed_channels:
            print(channel)

        self.check_for_conflicting_channels(subscribed_channels)
        self.deploy_prod_certificates(subscribed_channels)
        self.clean_up(subscribed_channels)

        self.write_migration_facts()

        if self.options.registration_state == "purge":
            print()
            print(_("Preparing to unregister system from legacy server..."))
            self.legacy_purge(rpc_session, session_key)
        elif self.options.registration_state == "unentitle":
            self.legacy_unentitle(rpc_session)
        else:
            # For the "keep" case, we just leave everything alone.
            pass

        using_systemd = self.is_using_systemd()
        self.handle_legacy_daemons(using_systemd)
        if self.options.remove_legacy_packages:
            self.remove_legacy_packages()

        identity = self.register(self.destination_creds, org, environment)
        if identity:
            self.enable_extra_channels(subscribed_channels)
示例#8
0
class MigrationEngine(object):
    def __init__(self, options):
        self.rhncfg = initUp2dateConfig()
        self.rhsmcfg = config.Config(initConfig())

        # Sometimes we need to send up the entire contents of the system id file
        # which is referred to in Satellite 5 nomenclature as a "certificate"
        # although it is not an X509 certificate.
        try:
            self.system_id_contents = open(self.rhncfg["systemIdPath"],
                                           'r').read()
        except IOError:
            system_exit(
                os.EX_IOERR,
                _("Could not read legacy system id at %s") %
                self.rhncfg["systemIdPath"])

        self.system_id = self.get_system_id(self.system_id_contents)

        self.proxy_host = None
        self.proxy_port = None
        self.proxy_user = None
        self.proxy_pass = None

        self.cp = None
        self.db = ProductDatabase()

        self.consumer_id = None

        self.options = options
        self.is_hosted = is_hosted()

    def authenticate(self, username, password, user_prompt, pw_prompt):
        if not username:
            username = six.moves.input(user_prompt).strip()
            readline.clear_history()

        if not password:
            password = getpass.getpass(prompt=pw_prompt)

        return UserCredentials(username, password)

    def get_auth(self):
        if self.options.registration_state == "keep":
            self.legacy_creds = UserCredentials(None, None)
        else:
            self.legacy_creds = self.authenticate(self.options.legacy_user,
                                                  self.options.legacy_password,
                                                  _("Legacy username: "******"Legacy password: "******"keep":
            self.destination_creds = self.authenticate(
                self.options.destination_user,
                self.options.destination_password, _("Destination username: "******"Destination password: "******"http://":
                http_proxy = http_proxy[7:]
            try:
                self.proxy_host, self.proxy_port = http_proxy.split(':')
            except ValueError as e:
                log.exception(e)
                system_exit(
                    os.EX_CONFIG,
                    _("Could not read legacy proxy settings.  ") +
                    SEE_LOG_FILE)

            if self.rhncfg['enableProxyAuth']:
                self.proxy_user = self.rhncfg['proxyUser']
                self.proxy_pass = self.rhncfg['proxyPassword']

            log.info("Using proxy %s:%s" % (self.proxy_host, self.proxy_port))
            if self.options.noproxy:
                # If the user doesn't want to use a proxy to connect to their subscription
                # management server, then remove any proxy information that may have crept in.
                self.rhsmcfg['server']['proxy_hostname'] = ''
                self.rhsmcfg['server']['proxy_port'] = ''
                self.rhsmcfg['server']['proxy_user'] = ''
                self.rhsmcfg['server']['proxy_password'] = ''
            else:
                self.rhsmcfg['server']['proxy_hostname'] = self.proxy_host
                self.rhsmcfg['server']['proxy_port'] = self.proxy_port
                self.rhsmcfg['server']['proxy_user'] = self.proxy_user or ''
                self.rhsmcfg['server'][
                    'proxy_password'] = self.proxy_pass or ''
            self.rhsmcfg.persist()

    def _get_connection_info(self):
        url_parse_error = os.EX_USAGE
        try:
            if self.options.destination_url is None:
                url_parse_error = os.EX_CONFIG
                hostname = self.rhsmcfg['server']['hostname']
                port = self.rhsmcfg['server'].get_int('port')
                prefix = self.rhsmcfg['server']['prefix']
            else:
                (_user, _password, hostname, port,
                 prefix) = parse_url(self.options.destination_url,
                                     default_port=443)
        except ServerUrlParseError as e:
            system_exit(url_parse_error,
                        _("Error parsing server URL: %s") % e.msg)

        connection_info = {
            'host': hostname,
            'ssl_port': int(port),
            'handler': prefix
        }

        if not self.options.noproxy:
            connection_info['proxy_hostname_arg'] = self.proxy_host
            connection_info['proxy_port_arg'] = self.proxy_port and int(
                self.proxy_port)
            connection_info['proxy_user_arg'] = self.proxy_user
            connection_info['proxy_password_arg'] = self.proxy_pass

        return connection_info

    def get_candlepin_connection(self, username, password):
        self.cp_provider = inj.require(inj.CP_PROVIDER)
        connection_info = self._get_connection_info()
        self.cp_provider.set_connection_info(**connection_info)

        if username and password:
            self.cp_provider.set_user_pass(username, password)
            return self.cp_provider.get_basic_auth_cp()

        return self.cp_provider.get_no_auth_cp()

    def check_ok_to_proceed(self):
        # check if this machine is already registered to Certicate-based RHN
        identity = inj.require(inj.IDENTITY)
        if identity.is_valid():
            if self.options.five_to_six:
                msgs = [
                    _("This system appears to already be registered to Satellite 6."
                      )
                ]
            else:
                msgs = [
                    _("This system appears to already be registered to Red Hat Subscription Management."
                      )
                ]
                msgs.append(
                    _("Please visit https://access.redhat.com/management/consumers/%s to view the profile details."
                      ) % identity.uuid)
            system_exit(1, msgs)

        try:
            self.cp.getStatus()
        except ssl.SSLError as e:
            print(
                _("The CA certificate for the destination server has not been installed."
                  ))
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

    def get_org(self, username):
        try:
            owner_list = self.cp.getOwnerList(username)
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

        if len(owner_list) == 0:
            system_exit(
                1,
                _("%s cannot register with any organizations.") % username)
        else:
            if self.options.org:
                org_input = self.options.org
            elif len(owner_list) == 1:
                org_input = owner_list[0]['key']
            else:
                org_input = six.moves.input(_("Org: ")).strip()
                readline.clear_history()

            org = None
            for owner_data in owner_list:
                if owner_data['key'] == org_input or owner_data[
                        'displayName'] == org_input:
                    org = owner_data['key']
                    break
            if not org:
                system_exit(os.EX_DATAERR,
                            _("Couldn't find organization '%s'.") % org_input)
        return org

    def get_environment(self, owner_key):
        environment_list = []
        try:
            if self.cp.supports_resource('environments'):
                environment_list = self.cp.getEnvironmentList(owner_key)
            elif self.options.environment:
                system_exit(
                    os.EX_UNAVAILABLE,
                    _("Environments are not supported by this server."))
        except Exception as e:
            log.exception(e)
            system_exit(os.EX_SOFTWARE, CONNECTION_FAILURE % e)

        environment = None
        if len(environment_list) > 0:
            if self.options.environment:
                env_input = self.options.environment
            elif len(environment_list) == 1:
                env_input = environment_list[0]['name']
            else:
                env_input = six.moves.input(_("Environment: ")).strip()
                readline.clear_history()

            for env_data in environment_list:
                # See BZ #978001
                if (env_data['name'] == env_input or
                    ('label' in env_data and env_data['label'] == env_input)
                        or ('displayName' in env_data
                            and env_data['displayName'] == env_input)):
                    environment = env_data['name']
                    break
            if not environment:
                system_exit(os.EX_DATAERR,
                            _("Couldn't find environment '%s'.") % env_input)

        return environment

    def connect_to_rhn(self, credentials):
        hostname = self.rhncfg['serverURL'].split('/')[2]
        server_url = 'https://%s/rpc/api' % (hostname)

        try:
            if self.rhncfg['enableProxy']:
                proxy = "%s:%s" % (self.proxy_host, self.proxy_port)
                log.info("Using proxy %s for legacy API methods" % (proxy))
                if self.rhncfg['enableProxyAuth']:
                    proxy = "@".join(
                        ["%s:%s" % (self.proxy_user, self.proxy_pass), proxy])
            else:
                proxy = None

            rpc_session = rpclib.Server(server_url, proxy=proxy)

            ca = self.rhncfg["sslCACert"]
            rpc_session.add_trusted_cert(ca)

            if credentials.username and credentials.password:
                session_key = rpc_session.auth.login(credentials.username,
                                                     credentials.password)
            else:
                session_key = None

            return (rpc_session, session_key)
        except Exception as e:
            log.exception(e)
            system_exit(
                1,
                _("Unable to authenticate to legacy server.  ") + SEE_LOG_FILE)

    def check_has_access(self, rpc_session, session_key):
        try:
            if session_key is None:
                # We should not ever be here.  This method has a guard that keeps it from being
                # called when not needed.  If we see this error, someone has made a programming
                # mistake.
                raise Exception(
                    "No session key available.  Check that XMLRPC connection is being made with credentials."
                )

            rpc_session.system.getDetails(session_key, self.system_id)
        except Exception as e:
            log.exception(e)
            system_exit(
                1,
                _("You do not have access to system %s.  ") % self.system_id +
                SEE_LOG_FILE)

    def resolve_base_channel(self, label, rpc_session, session_key):
        try:
            details = rpc_session.channel.software.getDetails(
                session_key, label)
        except Exception as e:
            log.exception(e)
            system_exit(
                os.EX_SOFTWARE,
                _("Problem encountered getting the list of subscribed channels.  "
                  ) + SEE_LOG_FILE)
        if details['clone_original']:
            return self.resolve_base_channel(details['clone_original'],
                                             rpc_session, session_key)
        return details

    def get_subscribed_channels_list(self, rpc_session, session_key):
        try:
            channels = getChannels().channels()
        except Exception as e:
            log.exception(e)
            system_exit(
                os.EX_SOFTWARE,
                _("Problem encountered getting the list of subscribed channels.  "
                  ) + SEE_LOG_FILE)
        if self.options.five_to_six:
            channels = [
                self.resolve_base_channel(c['label'], rpc_session, session_key)
                for c in channels
            ]
        return [x['label'] for x in channels]

    def print_banner(self, msg):
        print("\n+-----------------------------------------------------+")
        print(msg)
        print("+-----------------------------------------------------+")

    def check_for_conflicting_channels(self, subscribed_channels):
        jboss_channel = False
        for channel in subscribed_channels:
            if channel.startswith("jbappplatform"):
                if jboss_channel:
                    system_exit(
                        1,
                        _("You are subscribed to more than one jbappplatform channel."
                          "  This script does not support that configuration.")
                    )
                jboss_channel = True

    def get_release(self):
        f = open('/etc/redhat-release')
        lines = f.readlines()
        f.close()
        release = "RHEL-" + str(lines).split(' ')[6].split('.')[0]
        return release

    def read_channel_cert_mapping(self, mappingfile):
        f = open(mappingfile)
        lines = f.readlines()
        dic_data = {}
        for line in lines:
            # Lines should be of the format
            # key: value
            # Lines beginning with non-letters will be ignored
            if re.match("^[a-zA-Z]", line):
                line = line.replace("\n", "")
                key, val = line.strip().split(": ")
                dic_data[key] = val
        return dic_data

    def handle_collisions(self, applicable_certs):
        # if we have the same product IDs mapping to multiple certificates, we must abort.
        collisions = dict(
            (prod_id, mappings)
            for prod_id, mappings in list(applicable_certs.items())
            if len(mappings) > 1)
        if not collisions:
            return

        log.error("Aborting. Detected the following product ID collisions: %s",
                  collisions)
        self.print_banner(_("Unable to continue migration!"))
        print(
            _("You are subscribed to channels that have conflicting product certificates."
              ))
        for prod_id, mappings in list(collisions.items()):
            # Flatten the list of lists
            colliding_channels = [
                item for sublist in list(mappings.values()) for item in sublist
            ]
            print(_("The following channels map to product ID %s:") % prod_id)
            for c in sorted(colliding_channels):
                print("\t%s" % c)
        print(
            _("Reduce the number of channels per product ID to 1 and run migration again."
              ))
        print(
            _("To remove a channel, use 'rhn-channel --remove --channel=<conflicting_channel>'."
              ))
        sys.exit(1)

    def deploy_prod_certificates(self, subscribed_channels):
        release = self.get_release()
        mappingfile = "/usr/share/rhsm/product/" + release + "/channel-cert-mapping.txt"
        log.info("Using mapping file %s", mappingfile)

        try:
            dic_data = self.read_channel_cert_mapping(mappingfile)
        except IOError as e:
            log.exception(e)
            system_exit(
                os.EX_CONFIG,
                _("Unable to read mapping file: %(mappingfile)s.\n"
                  "Please check that you have the %(package)s package installed."
                  ) % {
                      "mappingfile": mappingfile,
                      "package": "subscription-manager-migration-data"
                  })

        applicable_certs = {}
        valid_rhsm_channels = []
        invalid_rhsm_channels = []
        unrecognized_channels = []

        for channel in subscribed_channels:
            try:
                if dic_data[channel] != 'none':
                    valid_rhsm_channels.append(channel)
                    cert = dic_data[channel]
                    log.info("Mapping found for: %s = %s", channel, cert)
                    prod_id = cert.split('-')[-1].split('.pem')[0]
                    cert_to_channels = applicable_certs.setdefault(prod_id, {})
                    cert_to_channels.setdefault(cert, []).append(channel)
                else:
                    invalid_rhsm_channels.append(channel)
                    log.info("%s is not mapped to any certificates", channel)
            except Exception:
                unrecognized_channels.append(channel)

        if invalid_rhsm_channels:
            self.print_banner(
                _("Channels not available on %s:") %
                self.options.destination_url)
            for i in invalid_rhsm_channels:
                print(i)

        if unrecognized_channels:
            self.print_banner(
                _("No product certificates are mapped to these legacy channels:"
                  ))
            for i in unrecognized_channels:
                print(i)

        if unrecognized_channels or invalid_rhsm_channels:
            if not self.options.force:
                system_exit(
                    1,
                    _("\nUse --force to ignore these channels and continue the migration.\n"
                      ))

        # At this point applicable_certs looks something like this
        # { '1': { 'cert-a-1.pem': ['channel1', 'channel2'], 'cert-b-1.pem': ['channel3'] } }
        # This is telling us that product ID 1 maps to two certificates, cert-a and cert-b.
        # Two channels map to the cert-a certificate and one channel maps to cert-b.
        # If we wind up in a situation where a user has channels that map to two different
        # certificates with the same product ID, (e.g. len(hash[product_id]) > 1) we've got a
        # collision and must abort.
        self.handle_collisions(applicable_certs)

        log.info("Certs to be installed: %s", applicable_certs)

        self.print_banner(
            _("Installing product certificates for these legacy channels:"))
        for i in valid_rhsm_channels:
            print(i)

        release = self.get_release()

        # creates the product directory if it doesn't already exist
        product_dir = inj.require(inj.PROD_DIR)
        db_modified = False
        for cert_to_channels in list(applicable_certs.values()):
            # At this point handle_collisions should have verified that len(cert_to_channels) == 1
            cert, channels = list(cert_to_channels.items())[0]
            source_path = os.path.join("/usr/share/rhsm/product", release,
                                       cert)
            truncated_cert_name = cert.split('-')[-1]
            destination_path = os.path.join(product_dir.path,
                                            truncated_cert_name)
            log.info("Copying %s to %s ", source_path, destination_path)
            shutil.copy2(source_path, destination_path)

            # See BZ #972883. Add an entry to the repo db telling subscription-manager
            # that packages for this product were installed by the RHN repo which
            # conveniently uses the channel name for the repo name.
            db_id = truncated_cert_name.split('.pem')[0]
            for chan in channels:
                self.db.add(db_id, chan)
                db_modified = True

        if db_modified:
            self.db.write()
        print(
            _("\nProduct certificates installed successfully to %s.") %
            product_dir.path)

    def clean_up(self, subscribed_channels):
        # Hack to address BZ 853233
        product_dir = inj.require(inj.PROD_DIR)
        if os.path.isfile(os.path.join(product_dir.path, "68.pem")) and \
            os.path.isfile(os.path.join(product_dir.path, "71.pem")):
            try:
                os.remove(os.path.join(product_dir.path, "68.pem"))
                self.db.delete("68")
                self.db.write()
                log.info("Removed 68.pem due to existence of 71.pem")
            except OSError as e:
                log.info(e)

        # Hack to address double mapping for 180.pem and 17{6|8}.pem
        is_double_mapped = [
            x for x in subscribed_channels if re.match(DOUBLE_MAPPED, x)
        ]
        is_single_mapped = [
            x for x in subscribed_channels if re.match(SINGLE_MAPPED, x)
        ]

        if is_double_mapped and is_single_mapped:
            try:
                os.remove(os.path.join(product_dir.path, "180.pem"))
                self.db.delete("180")
                self.db.write()
                log.info("Removed 180.pem")
            except OSError as e:
                log.info(e)

    def get_system_id(self, content):
        p = libxml2.parseDoc(content)
        system_id = int(
            p.xpathEval('string(//member[* = "system_id"]/value/string)').
            split('-')[1])
        return system_id

    def write_migration_facts(self):
        migration_date = datetime.now().isoformat()

        if not os.path.exists(FACT_FILE):
            f = open(FACT_FILE, 'w')
            json.dump(
                {
                    "migration.classic_system_id": self.system_id,
                    "migration.migrated_from": self.rhncfg['serverURL'],
                    "migration.migration_date": migration_date
                }, f)
            f.close()

    def disable_yum_rhn_plugin(self):
        # 'Inspired by' up2date_client/rhnreg.py
        """
        Disable yum-rhn-plugin by setting enabled=0 in file
        /etc/yum/pluginconf.d/rhnplugin.conf
        Can thrown IOError exception.
        """
        log.info("Disabling rhnplugin.conf")
        f = open(YUM_PLUGIN_CONF, 'r')
        lines = f.readlines()
        f.close()
        main_section = False
        f = open(YUM_PLUGIN_CONF, 'w')
        for line in lines:
            if re.match("^\[.*]", line):
                if re.match("^\[main]", line):
                    main_section = True
                else:
                    main_section = False
            if main_section:
                line = re.sub('^(\s*)enabled\s*=.+', r'\1enabled = 0', line)
            f.write(line)
        f.close()

    def legacy_unentitle(self, rpc_session):
        try:
            rpc_session.system.unentitle(self.system_id_contents)
        except Exception as e:
            log.exception(
                "Could not remove system entitlement on Satellite 5.", e)
            system_exit(
                os.EX_SOFTWARE,
                _("Could not remove system entitlement on legacy server.  ") +
                SEE_LOG_FILE)
        try:
            self.disable_yum_rhn_plugin()
        except Exception:
            pass

    def legacy_purge(self, rpc_session, session_key):
        system_id_path = self.rhncfg["systemIdPath"]

        log.info("Deleting system %s from legacy server...", self.system_id)
        try:
            result = rpc_session.system.deleteSystems(session_key,
                                                      self.system_id)
        except Exception:
            log.exception("Could not delete system %s from legacy server" %
                          self.system_id)
            # If we time out or get a network error, log it and keep going.
            shutil.move(system_id_path, system_id_path + ".save")
            print(
                _("Did not receive a completed unregistration message from legacy server for system %s."
                  ) % self.system_id)

            if self.is_hosted:
                print(
                    _("Please investigate on the Customer Portal at https://access.redhat.com."
                      ))
            return

        if result:
            log.info(
                "System %s deleted.  Removing system id file and disabling rhnplugin.conf",
                self.system_id)
            os.remove(system_id_path)
            try:
                self.disable_yum_rhn_plugin()
            except Exception:
                pass
            print(_("System successfully unregistered from legacy server."))
        else:
            # If the legacy server reports that deletion just failed, then quit.
            system_exit(
                1,
                _("Unable to unregister system from legacy server.  ") +
                SEE_LOG_FILE)

    def load_transition_data(self, rpc_session):
        try:
            transition_data = rpc_session.system.transitionDataForSystem(
                self.system_id_contents)
            self.consumer_id = transition_data['uuid']
        except Exception as e:
            log.exception(e)
            system_exit(
                1,
                _("Could not retrieve system migration data from legacy server.  "
                  ) + SEE_LOG_FILE)

    def consumer_exists(self, consumer_id):
        try:
            self.cp.getConsumer(consumer_id)
            return True
        except Exception as e:
            log.exception(e)
            print(
                _("Consumer %s doesn't exist.  Creating new consumer.") %
                consumer_id)
            return False

    def register(self, credentials, org, environment):
        # For registering the machine, use the CLI tool to reuse the username/password (because the GUI will prompt for them again)
        # Prepended a \n so translation can proceed without hitch
        print("")
        print(_("Attempting to register system to destination server..."))
        cmd = ['subscription-manager', 'register']

        # Candlepin doesn't want user credentials with activation keys
        # Auto-attach and environments are also forbidden
        if self.options.activation_keys:
            for key in self.options.activation_keys:
                cmd.append('--activationkey=' + key)
        else:
            cmd.append('--username='******'--password='******'--environment=' + environment)

            if self.options.auto:
                cmd.append('--auto-attach')

        if self.options.destination_url:
            cmd.append('--serverurl=' + self.options.destination_url)

        if org:
            cmd.append('--org=' + org)

        if self.options.five_to_six:
            if self.consumer_exists(self.consumer_id):
                cmd.append('--consumerid=' + self.consumer_id)

        if self.options.service_level:
            servicelevel = self.select_service_level(
                org, self.options.service_level)
            cmd.append('--servicelevel=' + servicelevel)

        subprocess.call(cmd)

        identity = inj.require(inj.IDENTITY)
        identity.reload()

        if not identity.is_valid():
            system_exit(
                2,
                _("\nUnable to register.\nFor further assistance, please contact Red Hat Global Support Services."
                  ))

        print(_("System '%s' successfully registered.\n") % identity.name)
        return identity

    def select_service_level(self, org, servicelevel):
        not_supported = _(
            "Error: The service-level command is not supported by the server.")
        try:
            levels = self.cp.getServiceLevelList(org)
        except RemoteServerException as e:
            system_exit(-1, not_supported)
        except RestlibException as e:
            if e.code == 404:
                # no need to die, just skip it
                print(not_supported)
                return None
            else:
                # server supports it but something went wrong, die.
                raise e

        # Create the sla tuple before appending the empty string to the list of
        # valid slas.
        slas = [(sla, sla) for sla in levels]
        # Display an actual message for the empty string level.
        slas.append((_("No service level preference"), ""))

        # The empty string is a valid level so append it to the list.
        levels.append("")
        if servicelevel is None or \
            servicelevel.upper() not in (level.upper() for level in levels):
            if servicelevel is not None:
                print(
                    _("\nService level \"%s\" is not available.") %
                    servicelevel)
            menu = Menu(
                slas,
                _("Please select a service level agreement for this system."))
            servicelevel = menu.choose()
        return servicelevel

    def enable_extra_channels(self, subscribed_channels):
        # Check if system was subscribed to extra channels like supplementary, optional, fastrack etc.
        # If so, enable them in the redhat.repo file
        extra_channels = {
            'supplementary': False,
            'productivity': False,
            'optional': False
        }
        for subscribedChannel in subscribed_channels:
            if 'supplementary' in subscribedChannel:
                extra_channels['supplementary'] = True
            elif 'optional' in subscribedChannel:
                extra_channels['optional'] = True
            elif 'productivity' in subscribedChannel:
                extra_channels['productivity'] = True

        if True not in list(extra_channels.values()):
            return

        # create and populate the redhat.repo file
        # use the injection cp_providers consumer auth
        repolib.RepoActionInvoker().update()

        # read in the redhat.repo file
        repofile = repolib.YumRepoFile()
        repofile.read()

        # enable any extra channels we are using and write out redhat.repo
        try:
            for rhsmChannel in repofile.sections():
                if ((extra_channels['supplementary']
                     and re.search('supplementary$', rhsmChannel))
                        or (extra_channels['optional']
                            and re.search('optional-rpms$', rhsmChannel))
                        or (extra_channels['productivity']
                            and re.search('productivity-rpms$', rhsmChannel))):
                    log.info("Enabling extra channel '%s'" % rhsmChannel)
                    repofile.set(rhsmChannel, 'enabled', '1')
            repofile.write()
        except Exception:
            print(_("\nCouldn't enable extra repositories."))
            command = "subscription-manager repos --help"
            print(
                _("Please ensure system has subscriptions attached, and see '%s' to enable additional repositories"
                  ) % command)

    def is_using_systemd(self):
        release_number = int(self.get_release().partition('-')[-1])
        return release_number > 6

    def is_daemon_installed(self, daemon, using_systemd):
        has_systemd_daemon = False
        if using_systemd:
            has_systemd_daemon = subprocess.call(
                "systemctl list-units %s.service | grep %s > /dev/null 2>&1" %
                (daemon, daemon),
                shell=True) == 0
        return has_systemd_daemon or os.path.exists("/etc/init.d/%s" % daemon)

    def is_daemon_running(self, daemon, using_systemd):
        if using_systemd:
            return subprocess.call("systemctl is-active --quiet %s" % daemon,
                                   shell=True) == 0
        else:
            return subprocess.call("service %s status > /dev/null 2>&1" %
                                   daemon,
                                   shell=True) == 0

    def handle_legacy_daemons(self, using_systemd):
        print(_("Stopping and disabling legacy services..."))
        log.info("Attempting to stop and disable legacy services: %s" %
                 " ".join(LEGACY_DAEMONS))
        for daemon in LEGACY_DAEMONS:
            if self.is_daemon_installed(daemon, using_systemd):
                self.disable_daemon(daemon, using_systemd)
                if self.is_daemon_running(daemon, using_systemd):
                    self.stop_daemon(daemon, using_systemd)

    def stop_daemon(self, daemon, using_systemd):
        if using_systemd:
            subprocess.call(["systemctl", "stop", daemon])
        else:
            subprocess.call(["service", daemon, "stop"])

    def disable_daemon(self, daemon, using_systemd):
        if using_systemd:
            subprocess.call(["systemctl", "disable", daemon])
        else:
            subprocess.call(["chkconfig", daemon, "off"])

    def remove_legacy_packages(self):
        print(_("Removing legacy packages..."))
        log.info("Attempting to remove legacy packages: %s" %
                 " ".join(LEGACY_PACKAGES))
        subprocess.call(["yum", "remove", "-q", "-y"] + LEGACY_PACKAGES)

    def main(self, args=None):
        self.get_auth()
        self.transfer_http_proxy_settings()
        self.cp = self.get_candlepin_connection(
            self.destination_creds.username, self.destination_creds.password)
        self.check_ok_to_proceed()

        (rpc_session, session_key) = self.connect_to_rhn(self.legacy_creds)
        if self.options.five_to_six:
            self.load_transition_data(rpc_session)
            org = None
            environment = None
        else:
            if self.options.activation_keys:
                environment = None
                org = self.options.org
            else:
                org = self.get_org(self.destination_creds.username)
                environment = self.get_environment(org)

        if self.options.registration_state != "keep":
            self.check_has_access(rpc_session, session_key)

        print()
        print(_("Retrieving existing legacy subscription information..."))
        subscribed_channels = self.get_subscribed_channels_list(
            rpc_session, session_key)
        self.print_banner(
            _("System is currently subscribed to these legacy channels:"))
        for channel in subscribed_channels:
            print(channel)

        self.check_for_conflicting_channels(subscribed_channels)
        self.deploy_prod_certificates(subscribed_channels)
        self.clean_up(subscribed_channels)

        self.write_migration_facts()

        if self.options.registration_state == "purge":
            print()
            print(_("Preparing to unregister system from legacy server..."))
            self.legacy_purge(rpc_session, session_key)
        elif self.options.registration_state == "unentitle":
            self.legacy_unentitle(rpc_session)
        else:
            # For the "keep" case, we just leave everything alone.
            pass

        using_systemd = self.is_using_systemd()
        self.handle_legacy_daemons(using_systemd)
        if self.options.remove_legacy_packages:
            self.remove_legacy_packages()

        identity = self.register(self.destination_creds, org, environment)
        if identity:
            self.enable_extra_channels(subscribed_channels)
 def test_product_database(self):
     Path.ROOT = "/mnt/sysimage"
     prod_db = ProductDatabase()
     self.assertEqual("/mnt/sysimage/var/lib/rhsm/productid.js",
                      prod_db.dir.abspath("productid.js"))