#!/usr/bin/python3

# Copyright (c) 2024, SUSE LLC, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library.

"""This script obtains information from the configured region server in the
   cloud environment and uses the information to register the guest with
   the SMT server based on the information provided by the region server.

   The configuration is in INI format and is located in
   /etc/regionserverclnt.cfg

   Logic:
   1.) Check if we are in the same region
       + Comparing information received from the region server and the
         cached data
   2.) Check if already registered
   3.) Register"""

import argparse
import ipaddress
import logging
import os
import requests
import sys
import time
import urllib.parse
import uuid

import cloudregister.registerutils as utils

from cloudregister import smt
from lxml import etree
from requests.auth import HTTPBasicAuth

LOG_FILE = '/var/log/cloudregister'
ZYPP_SERVICES = '/etc/zypp/services.d'

# Disable the urllib warnings
# We have server certs that have no subject alt names
# We have to check the server state API without certificate validation
requests.packages.urllib3.disable_warnings(
    requests.packages.urllib3.exceptions.InsecureRequestWarning
)
registration_returncode = 0


# ----------------------------------------------------------------------------
def register_modules(
    extensions, products, registration_target, instance_data_filepath,
    regcode='', registered=[], failed=[]
):
    """Register modules obeying dependencies"""
    global registration_returncode
    for extension in extensions:
        # If the extension is recommended it gets installed with the
        # baseproduct registration. No need to run another registration
        if extension.get('recommended'):
            register_modules(
                extension.get('extensions'), products, registration_target,
                instance_data_filepath, regcode, registered, failed
            )
            continue

        arch = extension.get('arch')
        identifier = extension.get('identifier')
        version = extension.get('version')
        triplet = '/'.join((identifier, version, arch))
        if triplet in products and triplet not in registered:
            registered.append(triplet)

            prod_reg = utils.register_product(
                registration_target=registration_target,
                regcode=regcode,
                product=triplet,
                instance_data_filepath=instance_data_filepath
            )

            if prod_reg.returncode:
                registration_returncode = prod_reg.returncode
                # Older versions of SUSEConnect wrote error messages to stdout
                error_message = prod_reg.output
                if not error_message:
                    error_message = prod_reg.error
                if (
                    registration_returncode == 67 and
                    (
                        'registration code' in error_message.lower() or
                        'system credentials' in error_message.lower()
                    )
                ):
                    # SUSEConnect sets the following exit codes:
                    # 0:  Registration successful
                    # 64: Connection refused
                    # 65: Access error, e.g. files not readable
                    # 66: Parser error: Server JSON response was not parseable
                    # 67: Server responded with error: see log output
                    failed.append(triplet)
                    # the registration should not be considered failed
                    # because of extra modules registered
                    registration_returncode = 0
                else:
                    logging.error('\tRegistration failed: %s' % error_message)

        register_modules(
            extension.get('extensions'), products, registration_target,
            instance_data_filepath, regcode, registered, failed
        )


# ----------------------------------------------------------------------------
def cleanup():
    """Remove any registration artifacts"""
    utils.clean_all()


# ----------------------------------------------------------------------------
def setup_registry(registration_target, clean='registry'):
    """
    Run the registration process for the container registry

    clean == all -> cleans repository and registry setup
    clean == registry -> clean only registry artifacts
    """
    user, password = utils.get_credentials(
        utils.get_credentials_file(registration_target)
    )
    registry_fqdn = registration_target.get_registry_FQDN()

    with_docker = utils.is_docker_present()
    if not with_docker:
        logging.info(
            'docker config file {} not found. '
            'Registration for docker skipped. '
            'Install docker to add docker support.'.format(
                utils.DOCKER_CONFIG_PATH
            )
        )

    if utils.is_registry_registered(registry_fqdn):
        if with_docker:
            # Special handling for the docker case:
            #
            # When docker is present we perform the registry configuration
            # for the docker container engine. This may cause calling the
            # setup routine even though docker was setup before. However,
            # the code will only effectively write a new registry setup
            # if the required modifications are not included.
            logging.info('Adding docker support')
            docker_setup_ok = utils.set_registries_conf_docker(registry_fqdn)

            # docker_setup_ok will take a True/False value on a real
            # action that happened. In any other case a None value is
            # present which indicates no action was performed.
            if docker_setup_ok is False:
                print(
                    'Registration failed(docker), see {0} for details'.format(
                        LOG_FILE
                    ), file=sys.stderr
                )
                sys.exit(1)
            elif docker_setup_ok is True:
                print('Successfully added docker to the registry setup')
        logging.info(
            'Instance is setup to access container registry, nothing to do'
        )
        return True
    logging.info(
        'Adding registry setup to instance, all existing shell '
        'sessions must be restarted to use the registry.'
    )
    registry_setup_ok = False
    if utils.prepare_registry_setup(registry_fqdn, user, password):
        logging.info('Adding podman support')
        registry_setup_ok = utils.set_registries_conf_podman(registry_fqdn)
        if with_docker:
            logging.info('Adding docker support')
            registry_setup_ok = utils.set_registries_conf_docker(registry_fqdn)
        if utils.is_suma_instance():
            registry_setup_ok = utils.set_registry_fqdn_suma(registry_fqdn)
    if not registry_setup_ok:
        if clean == 'all':
            cleanup()
        elif clean == 'registry':
            utils.clean_registry_setup()
        print(
            'Registration failed(registry), see {0} for details'.format(
                LOG_FILE
            ), file=sys.stderr
        )
        sys.exit(1)
    print('Instance registry setup done, sessions must be restarted !')


# ----------------------------------------------------------------------------
def get_responding_update_server(region_smt_servers):
    """
    Figure out which update server is responsive
    If no update server responds, exit with an error message
    that lists the tested servers
    """
    tested_smt_servers = []
    for smt_srv in region_smt_servers:
        tested_smt_servers.append((smt_srv.get_ipv4(), smt_srv.get_ipv6()))
        if smt_srv.is_responsive():
            utils.set_as_current_smt(smt_srv)
            return smt_srv
    logging.error(
        'No response from: {}'.format(tested_smt_servers)
    )
    sys.exit(1)


# ----------------------------------------------------------------------------
def setup_ltss_registration(
    registration_target, regcode, instance_data_filepath
):
    """
    Run registration command to register LTSS for this instance
    """
    logging.info('Running LTSS registration...')
    print('Running LTSS registration...this takes a little longer')
    product = utils.get_product_tree()
    if product is None:
        message = 'Cannot find baseproduct registration for LTSS'
        logging.error(message)
        print(message, file=sys.stderr)
        sys.exit(1)

    ltss_registered = False
    if os.path.isdir(ZYPP_SERVICES):
        for entry in os.listdir(ZYPP_SERVICES):
            if 'LTSS' in entry:
                ltss_registered = True
                break
    if ltss_registered:
        message = 'LTSS registration succeeded'
        logging.info(message)
        print(message)
    else:
        base_product = utils.get_product_triplet(product)
        # LTSS registration is always SLES even for variants such as
        # SAP and HPC.
        ltss_product_triplet = 'SLES-LTSS/{0}/{1}'.format(
            base_product.version, base_product.arch
        )

        prod_reg = utils.register_product(
            registration_target=registration_target,
            product=ltss_product_triplet,
            regcode=regcode,
            instance_data_filepath=instance_data_filepath
        )
        if prod_reg.returncode != 0:
            # Something went wrong...
            # Even on error SUSEConnect writes messages to stdout, go figure
            message = prod_reg.output
            logging.error('LTSS registration failed')
            logging.error('\t{0}'.format(message))
            print(
                'LTSS registration failed see /var/log/cloudregister '
                'for details', file=sys.stderr
            )
            sys.exit(1)
        message = 'LTSS registration succeeded'
        logging.info(message)
        print(message)


# ----------------------------------------------------------------------------
def register_base_product(
    registration_target, instance_data_filepath, args, region_smt_servers
):
    """Register the base product and return the RMT update server registered."""
    global registration_returncode
    base_registered = False
    failed_smts = []
    while not base_registered:
        utils.add_hosts_entry(registration_target)
        prod_reg = utils.register_product(
            registration_target=registration_target,
            email=args.email,
            regcode=args.reg_code,
            instance_data_filepath=instance_data_filepath
        )
        if prod_reg.returncode:
            registration_returncode = prod_reg.returncode
            # Even on error SUSEConnect writes messages to stdout, go figure
            error_message = prod_reg.output
            failed_smts.append(registration_target.get_ipv4())
            if (
                len(failed_smts) == len(region_smt_servers) or
                (registration_returncode == 67 and
                 'registration code' in error_message.lower() and
                 args.reg_code)
            ):
                # there are no more RMT servers to try to register to or
                # registration failed because of an invalid reg code
                # and that SCC response will not change, independently
                # of the RMT sibling selected
                # SCC exit codes:
                # 0: Registration successful
                # 64: Connection refused
                # 65: Access error, e.g. files not readable
                # 66: Parser error: Server JSON response was not parseable
                # 67: Server responded with error: see log output
                logging.error('Baseproduct registration failed')
                logging.error('\t%s' % error_message)
                cleanup()
                print(error_message, file=sys.stderr)
                sys.exit(1)
            for smt_srv in region_smt_servers:
                target_smt_ipv4 = registration_target.get_ipv4()
                target_smt_ipv6 = registration_target.get_ipv6()
                new_smt_ipv4 = smt_srv.get_ipv4()
                new_smt_ipv6 = smt_srv.get_ipv6()
                if (
                        smt_srv.get_ipv4() != registration_target.get_ipv4() and
                        smt_srv.get_ipv4() not in failed_smts
                ):
                    error_msg = 'Registration with %s failed. Trying %s'
                    logging.error(
                        error_msg % (
                            str((target_smt_ipv4, target_smt_ipv6)),
                            str((new_smt_ipv4, new_smt_ipv6))
                        )
                    )
                    utils.remove_registration_data()
                    utils.clean_hosts_file(
                        registration_target.get_domain_name()
                    )
                    registration_target = smt_srv
                    break
        else:
            logging.info('Baseproduct registration complete')
            base_registered = True
            utils.clear_new_registration_flag()
            if args.email or args.reg_code:
                utils.set_rmt_as_scc_proxy_flag()

    return registration_target


# ----------------------------------------------------------------------------
def find_alive_registration_target(registration_smt, region_smt_servers):
    target_found = False
    if registration_smt:
        registration_smt_cache_file_name = (
            os.path.join(
                utils.get_state_dir(),
                utils.REGISTERED_SMT_SERVER_DATA_FILE_NAME
            )
        )

        updated = utils.update_rmt_cert(registration_smt)
        alive = registration_smt.is_responsive()
        if alive:
            msg = 'Instance is registered, and update server is reachable, '
            msg += 'nothing to do'
            # The cache data may have been cleared, write if necessary
            if not os.path.exists(registration_smt_cache_file_name) or updated:
                utils.store_smt_data(
                    registration_smt_cache_file_name,
                    registration_smt
                )
                if not utils.has_rmt_in_hosts(registration_smt):
                    utils.clean_hosts_file(registration_smt.get_domain_name())
                    utils.add_hosts_entry(registration_smt)
            # The registry target may be missing, write if necessary
            if not utils.has_registry_in_hosts(registration_smt):
                utils.clean_hosts_file(registration_smt.get_domain_name())
                utils.add_hosts_entry(registration_smt)
            logging.info(msg)
            target_found = True
        else:
            # The current target server is not resposive, lets check if we can
            # find another server
            for new_target in region_smt_servers:
                if (
                        not new_target.is_responsive() or
                        not registration_smt.is_equivalent(new_target)
                ):
                    continue
                smt_ip = new_target.get_ipv4()
                if utils.has_rmt_ipv6_access(new_target):
                    smt_ip = new_target.get_ipv6()
                msg = 'Configured update server is unresponsive, switching '
                msg += 'to equivalent update server with ip %s' % smt_ip
                utils.replace_hosts_entry(registration_smt, new_target)
                utils.store_smt_data(
                    registration_smt_cache_file_name,
                    new_target
                )
                utils.update_rmt_cert(new_target)
                registration_smt = new_target
                target_found = True
                break
            else:
                msg = 'Configured update server is unresponsive. '
                msg += 'Could not find a replacement update server '
                msg += 'in this region. Possible network configuration issue'
                logging.error(msg)
                sys.exit(1)

    return registration_smt, target_found


# ----------------------------------------------------------------------------
def make_request(url, registration_target):
    headers = {'Accept': 'application/vnd.scc.suse.com.v4+json'}
    user, password = utils.get_credentials(
        utils.get_credentials_file(registration_target)
    )
    res = requests.get(
        url,
        auth=HTTPBasicAuth(user, password),
        headers=headers
    )
    if res.status_code != 200:
        err_msg = 'Unable to obtain product information from server "%s"\n'
        err_msg += '\t%s\n\t%s\nUnable to register modules, exiting.'
        ips = '%s,%s' % (
            registration_target.get_ipv4(), registration_target.get_ipv6()
        )
        logging.error(err_msg % (ips, res.reason, res.content.decode("UTF-8")))
        sys.exit(1)

    return res.json()


# ----------------------------------------------------------------------------
def get_system_products(registration_target):
    base_product = utils.get_product_triplet(
        utils.get_product_tree()
    )
    query_args = 'identifier=%s&version=%s&arch=%s' % (
        base_product.name, base_product.version, base_product.arch
    )
    system_products_url = 'https://%s/connect/systems/products?%s' % (
        registration_target.get_FQDN(), query_args
    )
    # request the system products
    return make_request(system_products_url, registration_target)


# ----------------------------------------------------------------------------
def get_extensions(registration_target):
    prod_data = get_system_products(registration_target)
    return prod_data.get('extensions')


# ----------------------------------------------------------------------------
def get_proxies():
    """Return the proxies set in the environment in a dictionary or None."""
    proxies = None
    if utils.set_proxy():
        http_proxy = os.environ.get('http_proxy')
        https_proxy = os.environ.get('https_proxy')
        no_proxy = os.environ.get('no_proxy')
        proxies = {
            'http_proxy': http_proxy,
            'https_proxy': https_proxy,
            'no_proxy': no_proxy
        }
        logging.info('Using proxy settings: %s' % proxies)

    return proxies


# ----------------------------------------------------------------------------
def get_region_smt_data(args, cfg, proxies):
    """Return the region SMT data."""
    if args.user_smt_ip:
        smt_xml = '<regionSMTdata><smtInfo '
        smt_xml += 'fingerprint="%s" ' % args.user_smt_fp
        smt_ip = ipaddress.ip_address(args.user_smt_ip)
        if isinstance(smt_ip, ipaddress.IPv6Address):
            smt_xml += 'SMTserverIPv6="%s" ' % args.user_smt_ip
        elif isinstance(smt_ip, ipaddress.IPv4Address):
            smt_xml += 'SMTserverIP="%s" ' % args.user_smt_ip
        smt_xml += 'SMTserverName="%s" ' % args.user_smt_fqdn
        registry_fqdn = 'registry-{}'.format(args.user_smt_fqdn.split('-')[1])
        smt_xml += 'SMTregistryName="%s"' % registry_fqdn
        smt_xml += '/></regionSMTdata>'
        return etree.fromstring(smt_xml)
    else:
        return utils.fetch_smt_data(cfg, proxies)


# ----------------------------------------------------------------------------
def get_update_servers(region_smt_data, cfg):
    """Return available update servers in a list."""
    region_smt_servers = []
    https_only = utils.https_only(cfg)
    for cnt, child in enumerate(region_smt_data, start=1):
        smt_server = smt.SMT(child, https_only)
        region_smt_servers.append(smt_server)
        # Write the available servers to cache as well
        utils.store_smt_data(
            os.path.join(
                utils.get_state_dir(),
                utils.AVAILABLE_SMT_SERVER_DATA_FILE_NAME % cnt
            ),
            smt_server
        )

    return region_smt_servers


# ----------------------------------------------------------------------------
argparse = argparse.ArgumentParser(description='Register on-demand instance')
argparse.add_argument(
    '--clean',
    action='store_true',
    dest='clean_up',
    default=False,
    help='Clean up registration data'
)
argparse.add_argument(
    '-d', '--delay',
    default=0,
    dest='delay_time',
    help='Delay the start of registration by given value in seconds',
    type=int
)
# default config file location not set it is encoded in the utils function
# get_config(()
argparse.add_argument(
    '-f', '--config-file',
    dest='config_file',
    help='Path to config file, default: /etc/regionserverclnt.cfg',
)
argparse.add_argument(
    '--force-new',
    action='store_true',
    dest='force_new_registration',
    default=False,
    help='Force a new registration, for exceptional cases only',
)
help_msg = 'The target update server cert fingerprint. '
help_msg += 'Use in exceptional cases only'
argparse.add_argument(
    '--smt-fp',
    dest='user_smt_fp',
    help=help_msg
)
help_msg = 'The target update server FQDN. '
help_msg += 'Use in exceptional cases only'
argparse.add_argument(
    '--smt-fqdn',
    dest='user_smt_fqdn',
    help=help_msg
)
help_msg = 'The target update server IP. '
help_msg += 'Use in exceptional cases only'
argparse.add_argument(
    '--smt-ip',
    dest='user_smt_ip',
    help=help_msg
)
help_msg = 'Email address for product registration'
argparse.add_argument(
    '-e', '--email',
    dest='email',
    help=help_msg
)
help_msg = 'The registration code'
argparse.add_argument(
    '-r', '--regcode',
    dest='reg_code',
    help=help_msg
)
argparse.add_argument(
    '-v', '--version',
    action='version',
    version='{version}'.format(
        version=open(
            os.path.join(
                os.path.dirname(smt.__file__),
                'VERSION'
            )
        ).read().strip()
    )
)


def main(args):
    global registration_returncode  # noqa: F824
    if args.user_smt_ip or args.user_smt_fqdn or args.user_smt_fp:
        if not (args.user_smt_ip and args.user_smt_fqdn and args.user_smt_fp):
            msg = '--smt-ip, --smt-fqdn, and --smt-fp must be used together'
            print(msg, file=sys.stderr)
            sys.exit(1)

        try:
            ipaddress.ip_address(args.user_smt_ip)
        except ValueError as err:
            msg = "--smt-ip value '{ip_addr}' is not correct: {err}".format(
                ip_addr=args.user_smt_ip,
                err=err
            )
            print(msg, file=sys.stderr)
            sys.exit(1)

        if not utils.has_network_access_by_ip_address(args.user_smt_ip):
            error_message = (
                'Connection error: Could not establish a connection to {ip}.'
                'Please, make sure the network configuration supports the '
                'provided {ip} address version.'.format(ip=args.user_smt_ip)
            )
            print(error_message, file=sys.stderr)
            sys.exit(1)

    if args.clean_up and args.force_new_registration:
        msg = '--clean and --force-new are incompatible, use one or the other'
        print(msg, file=sys.stderr)
        sys.exit(1)

        # Specifying reg code only works, but an e-mail requires a reg code
    if (args.email and not args.reg_code):
        msg = '--email and --regcode must be used together'
        print(msg, file=sys.stderr)
        sys.exit(1)

    time.sleep(int(args.delay_time))

    config_file = args.config_file
    if config_file:
        config_file = os.path.expanduser(args.config_file)
    cfg = utils.get_config(config_file)
    utils.start_logging()

    if args.clean_up:
        logging.info('Registration clean up initiated by user')
        cleanup()
        sys.exit(0)

    if not os.path.isdir(utils.get_state_dir()):
        os.makedirs(utils.get_state_dir())

    utils.set_new_registration_flag()
    if args.force_new_registration:
        logging.info('Forced new registration')

    if args.user_smt_ip:
        msg = 'Using user specified SMT server:\n'
        msg += '\n\t"IP:%s"' % args.user_smt_ip
        msg += '\n\t"FQDN:%s"' % args.user_smt_fqdn
        msg += '\n\t"Fingerprint:%s"' % args.user_smt_fp
        logging.info(msg)

    cached_smt_servers = utils.get_available_smt_servers()
    if cached_smt_servers:
        # If we have an update server cache the system is registered in
        # some way shape or form
        utils.clear_new_registration_flag()
    else:
        utils.write_framework_identifier(cfg)

    # Forced registration or user specified SMT, clear existing registration
    # data
    if (args.force_new_registration and cached_smt_servers) or args.user_smt_ip:
        if utils.is_zypper_running():
            msg = 'zypper is running: Registration with the update '
            msg += 'infrastructure is only possible if zypper is not running.\n'
            msg += 'Please re-run the force registration process after zypper '
            msg += 'has completed'
            print(msg)
            sys.exit(1)
        cleanup()
        utils.set_new_registration_flag()
        utils.write_framework_identifier(cfg)
        cached_smt_servers = []

    # Proxy setup
    proxies = get_proxies()

    region_smt_data = get_region_smt_data(args, cfg, proxies)
    registration_smt = utils.get_current_smt()

    # Check if we are in the same region
    region_smt_servers = cached_smt_servers
    region_change = utils.has_region_changed(cfg)
    if region_change and utils.uses_rmt_as_scc_proxy():
        # We do not have the users registration code, stay connected to the
        # servers in a different region.
        # If the user also moved to a new framework registration will be broken
        # due to the instance data.
        # This code is a safe guard in case a user enabled the service on a BYOS
        # instance, which is not supposed to be the case or tries
        print('Region change detected:')
        print(
            '\tSystem uses SCC credentials, please re-register the system'
            ' to the update infrastrusture in this region\n'
            '\tregistercloudguest --clean\n'
            '\tregistercloudguest -r YOUR_REG_CODE'
        )
    elif region_change:
        logging.info('Region change detected, registering to new servers')
        cleanup()
        region_smt_servers = cached_smt_servers = []
        registration_smt = None
        utils.set_new_registration_flag()
        utils.write_framework_identifier(cfg)

    if not region_smt_servers:
        region_smt_servers = get_update_servers(region_smt_data, cfg)

    # Check if the target RMT for the registration is alive or if we can
    # find a server that is alive in this region
    registration_smt, registration_target_found = find_alive_registration_target(  # noqa: E501
        registration_smt, region_smt_servers
    )

    # Check if we need to send along any instance data
    instance_data_filepath = ''
    instance_data = utils.get_instance_data(cfg)
    if instance_data:
        instance_data_filepath = os.path.join(
            utils.get_state_dir(), str(uuid.uuid4())
        )
        inst_data_out = open(instance_data_filepath, 'w')
        inst_data_out.write(instance_data)
        inst_data_out.close()

    if registration_target_found:
        # The system is properly registered, run through the checklist now:
        # 1. check/setup the container registry for this target
        setup_registry(registration_smt)

        # 2. check/setup if we got a regcode which is handled as LTSS
        if args.reg_code:
            setup_ltss_registration(
                registration_smt,
                args.reg_code,
                instance_data_filepath
            )

        # All done, time to leave...
        sys.exit(0)

    # We should not get here for a registered system that is a proxy. However,
    # it doesn't hurt to check and get out before breaking things
    if utils.uses_rmt_as_scc_proxy():
        logging.info('System already uses the update infrastructure with a '
                     'registration code, nothing to do')
        sys.exit(0)

    # The system is not yet registered, Figure out which server
    # is responsive and use it as registration target
    registration_target = get_responding_update_server(region_smt_servers)

    # Create location to store data if it does not exist
    if not os.path.exists(utils.get_state_dir()):
        os.system('mkdir -p %s' % utils.get_state_dir())

    # Write the data of the current target server
    utils.set_as_current_smt(registration_target)

    # Check if registration is supported
    if not utils.is_registration_supported(cfg):
        sys.exit(0)

    register_cmd = utils.get_register_cmd()
    if not (os.path.exists(register_cmd) and os.access(register_cmd, os.X_OK)):
        err_msg = 'No registration executable found'
        logging.error(err_msg)
        print(err_msg, file=sys.stderr)
        sys.exit(1)

    # get product list
    products = utils.get_installed_products()
    if products is None:
        logging.error('No products installed on system')
        sys.exit(1)

    if not utils.import_smt_cert(registration_target):
        logging.error('SMT certificate import failed')
        sys.exit(1)

    # Register the base product first
    registration_target = register_base_product(
        registration_target, instance_data_filepath, args, region_smt_servers
    )
    extensions = get_extensions(registration_target)
    failed_extensions = []
    register_modules(
        extensions,
        products,
        registration_target,
        instance_data_filepath,
        args.reg_code,
        failed=failed_extensions
    )
    if os.path.exists(instance_data_filepath):
        os.unlink(instance_data_filepath)

    if registration_returncode:
        cleanup()
        print(
            'Registration failed(repository), see {0} for details'.format(
                LOG_FILE
            ), file=sys.stderr
        )
        sys.exit(registration_returncode)

    # Setup container registry
    setup_registry(registration_target)

    print('Registration succeeded')

    if failed_extensions:
        print(
            'There are products that were not registered because they need '
            'an additional registration code, to register them please run '
            'the following command:'
        )
        activate_prod_cmd = 'SUSEConnect -p {} -r ADDITIONAL REGCODE'
        if utils.is_transactional_system():
            activate_prod_cmd = 'transactional-update register -p {} '
            activate_prod_cmd += '-r ADDITIONAL REGCODE'
        for failed_extension in failed_extensions:
            print(activate_prod_cmd.format(failed_extension))

    # Enable Nvidia repo if repo(s) are configured
    # and destination can be reached
    if utils.has_nvidia_support():
        nvidia_repo_names = utils.find_repos('nvidia')
        for repo_name in nvidia_repo_names:
            url = urllib.parse.urlparse(utils.get_repo_url(repo_name))
            cmd = ['ping', '-c', '2', url.hostname]
            if utils.exec_subprocess(cmd):
                msg = (
                    'Cannot reach host: "{hostname}", '
                    'will not enable repo "{repo_name}"'
                ).format(hostname=url.hostname, repo_name=repo_name)
                logging.info(msg)
            else:
                utils.enable_repository(repo_name)


if __name__ == '__main__':  # pragma: no cover
    args = argparse.parse_args()
    main(args)
    utils.switch_services_to_plugin()
