Пример #1
0
 def __init__(self, host, username, password, port=22, register_type='rhsm',
              org=None, activation_key=None):
     """
     Define the global variables.
     :param host: ip/hostname to run subscription-manager command.
     :param username: account username of the host
     :param password: password to access the host
     :param port: port to access the host
     :param register_type: rhsm/satellite, rhsm as default
     :param org: organization of the entitlement server
     :param activation_key: activation_key of the satellite server.
         Will register by activation_key when the value is set.
     """
     self.host = host
     self.register_type = register_type
     self.rhsm_conf = '/etc/rhsm/rhsm.conf'
     self.ssh = SSHConnect(host=host, user=username, pwd=password, port=port)
     register = get_register_handler(register_type)
     self.server = register.server
     self.username = register.username
     self.password = register.password
     self.port = register.port
     self.prefix = register.prefix
     self.org = org or register.default_org
     self.activation_key = activation_key
     if register_type != 'satellite':
         self.baseurl = register.baseurl
Пример #2
0
def install_rhel_by_grup(args):
    """
    Install a rhel system beased on an available host by change the grub files.
    Please refer to the utils/README for usage.
    """
    ssh_nfs = SSHConnect(host=config.nfs.server,
                         user=config.nfs.username,
                         pwd=config.nfs.password)
    ssh_host = SSHConnect(host=args.server,
                          user=args.username,
                          pwd=args.password)
    repo_base, repo_extra = base.rhel_compose_url(args.rhel_compose)
    ks_url, ks_path, vmlinuz_url, initrd_url = grup_params(repo_base)
    try:
        ks_file_create(ssh_nfs, ks_path, repo_base, repo_extra)
        grub_update(ssh_host, ks_url, vmlinuz_url, initrd_url, repo_base)
        grub_reboot(ssh_host)
        for i in range(60):
            if base.host_ping(args.server):
                base.ssh_connect(ssh_host)
                base.rhel_compose_repo(ssh_host, args.rhel_compose,
                                       '/etc/yum.repos.d/compose.repo')
            time.sleep(30)
    except Exception as e:
        logger.error(e)
    finally:
        ssh_host.runcmd(f'rm -rf {ks_path}')
Пример #3
0
def satellite_deploy(args):
    """
    Deploy satellite server by cdn or dogfood with required arguments.
    Please refer to the README for usage.
    """
    sat_ver = args.version
    sat_repo = args.repo
    rhel_ver = args.rhel_compose.split('-')[1].split('.')[0]
    ssh = SSHConnect(host=args.server,
                     user=args.ssh_username,
                     pwd=args.ssh_password)
    system_init(ssh, 'satellite')

    # Enable repos of cnd or dogfood
    if 'cdn' in sat_repo:
        sm = SubscriptionManager(host=args.server,
                                 username=args.ssh_username,
                                 password=args.ssh_password,
                                 register_type='rhsm')
        satellite_repo_enable_cdn(sm, rhel_ver, sat_ver)
    if 'dogfood' in sat_repo:
        satellite_repo_enable_dogfood(ssh, rhel_ver, sat_ver)

    # Install satellite
    satellite_pkg_install(ssh)
    satellite_installer(ssh, args.admin_password)

    # Upload manifest as requirement
    if args.manifest:
        satellite_manifest_upload(ssh, args.manifest, args.admin_username,
                                  args.admin_password)
    logger.info(f'Succeeded to deploy satellite ({sat_ver})')
Пример #4
0
def install_rhel_by_beaker(args):
    """
    Install rhel os by submitting job to beaker with required arguments.
    Please refer to the utils/README for usage.
    """
    job_name = f'virtwho-{args.rhel_compose}'
    ssh_client = SSHConnect(
        host=config.beaker.client,
        user=config.beaker.client_username,
        pwd=config.beaker.client_password
    )
    beaker_client_kinit(
        ssh_client,
        config.beaker.keytab,
        config.beaker.principal
    )
    job_id = beaker_job_submit(
        ssh_client,
        job_name,
        args.rhel_compose,
        args.arch,
        args.variant,
        args.job_group,
        args.host,
        args.host_type,
        args.host_require,
    )
    while beaker_job_status(ssh_client, job_name, job_id):
        time.sleep(60)
    host = beaker_job_result(ssh_client, job_name, job_id)
    if host:
        logger.info(f'Succeeded to install {args.rhel_compose} by beaker '
                    f'({host})')
        return host
    raise FailException('Failed to install {args.rhel_compose} by beaker')
Пример #5
0
 def create(self):
     """create virt-who config file under /etc/virt-who.d/.
     """
     if self.mode == 'local':
         self.update('type', 'libvirt')
     else:
         self.update('type', self.mode)
         self.update('hypervisor_id', 'hostname')
     if self.mode == 'kubevirt':
         self.update('kubeconfig', self.hypervisor.config_file)
     if self.mode in ('esx', 'xen', 'hyperv', 'rhevm', 'libvirt', 'ahv'):
         if self.mode == 'rhevm':
             ssh_rhevm = SSHConnect(host=self.hypervisor.server,
                                    user=self.hypervisor.ssh_username,
                                    pwd=self.hypervisor.ssh_password)
             self.hypervisor.server = f'''https://{hostname_get(ssh_rhevm)}:
                                          443/ovirt-engine'''
         self.update('server', self.hypervisor.server)
         self.update('username', self.hypervisor.username)
         self.update('password', self.hypervisor.password)
     self.update('rhsm_hostname', self.register.server)
     self.update('rhsm_username', self.register.username)
     self.update('rhsm_password', self.register.password)
     self.update('rhsm_prefix', self.register.prefix)
     self.update('rhsm_port', self.register.port)
     self.update('owner', self.register.default_org)
Пример #6
0
 def __init__(self, org=None, activation_key=None):
     """
     Using hammer command to set satellite, handle organization and
     activation key, attach/remove subscription for host.
     Using api to check the host-to-guest associations.
     :param org: organization label, use the default_org configured
         in virtwho.ini as default.
     :param activation_key: activation key name, use the configure
         in virtwho.ini as default.
     """
     register = get_register_handler('satellite')
     self.org = org or register.default_org
     self.activation_key = activation_key or register.activation_key
     self.ssh = SSHConnect(host=register.server,
                           user=register.ssh_username,
                           pwd=register.ssh_password)
     self.hammer = 'hammer --output=json'
     self.org_id = self.organization_id()
     self.api = f'https://{register.server}'
     self.auth = (register.username, register.password)
Пример #7
0
def libvirt_access_no_password(ssh):
    """
    Configure virt-who host accessing remote libvirt host by ssh
    without password.
    :param ssh: ssh access of virt-who host
    """
    ssh_libvirt = SSHConnect(host=config.libvirt.server,
                             user=config.libvirt.username,
                             pwd=config.libvirt.password)
    _, _ = ssh.runcmd('echo -e "\n" | ' 'ssh-keygen -N "" &> /dev/null')
    ret, output = ssh.runcmd('cat ~/.ssh/id_rsa.pub')
    if ret != 0 or output is None:
        raise FailException("Failed to create ssh key")
    _, _ = ssh_libvirt.runcmd(f"mkdir ~/.ssh/;"
                              f"echo '{output}' >> ~/.ssh/authorized_keys")
    ret, _ = ssh.runcmd(f'ssh-keyscan -p 22 {config.libvirt.server} >> '
                        f'~/.ssh/known_hosts')
    if ret != 0:
        raise FailException(
            'Failed to configure access libvirt without passwd')
Пример #8
0
def provision_virtwho_host(args):
    """
    Configure virt-who host for an existing server or a new one installed by beaker.
    Please refer to the provision/README for usage.
    """
    logger.info("+++ Start to deploy the virt-who host +++")

    if args.gating_msg:
        msg = base.gating_msg_parser(args.gating_msg)
        args.virtwho_pkg_url = msg['pkg_url']
        if not args.rhel_compose:
            args.rhel_compose = msg['latest_rhel_compose']
        config.update('gating', 'package_nvr', msg['pkg_nvr'])
        config.update('gating', 'build_id', msg['build_id'])
        config.update('gating', 'task_id', msg['task_id'])
    # Will deploy a new host by beaker if no server provided
    if not args.server:
        beaker_args_define(args)
        args.server = install_rhel_by_beaker(args)
        args.username = config.beaker.default_username
        args.password = config.beaker.default_password
    ssh_host = SSHConnect(host=args.server,
                          user=args.username,
                          pwd=args.password)
    # Initially set the host
    base.rhel_compose_repo(ssh_host, args.rhel_compose,
                           '/etc/yum.repos.d/compose.repo')
    base.system_init(ssh_host, 'virtwho')
    virtwho_pkg = virtwho_install(ssh_host, args.virtwho_pkg_url)
    # Update the test properties in virtwho.ini
    config.update('job', 'rhel_compose', args.rhel_compose)
    config.update('virtwho', 'server', args.server)
    config.update('virtwho', 'username', args.username)
    config.update('virtwho', 'password', args.password)
    config.update('virtwho', 'package', virtwho_pkg)
    # Configure the virt-who host as mode requirements
    if (config.job.hypervisor == 'libvirt'
            or 'libvirt' in config.job.multi_hypervisors):
        libvirt_access_no_password(ssh_host)

    if (config.job.hypervisor == 'kubevirt'
            or 'kubevirt' in config.job.multi_hypervisors):
        kubevirt_config_file(ssh_host)

    if (config.job.hypervisor == 'local'
            or 'local' in config.job.multi_hypervisors):
        config.update('local', 'server', args.server)
        config.update('server', 'username', args.username)
        config.update('server', 'password', args.password)

    logger.info(f"+++ Suceeded to deploy the virt-who host "
                f"{args.rhel_compose}/{args.server} +++")
Пример #9
0
def virtwho_ssh_connect(mode=None):
    """Define the ssh connection of virt-who host, get data from
    virtwho.ini file.
    :param mode: The test hypervisor mode.
    """
    virtwho = config.virtwho
    if mode == 'local':
        virtwho = config.local
    host = virtwho.server
    username = virtwho.username
    password = virtwho.password
    port = virtwho.port or 22
    return SSHConnect(host=host, user=username, pwd=password, port=port)
Пример #10
0
def satellite_deploy_for_virtwho(args):
    """
    Deploy satellite by cdn or dogfood with required arguments.
    If no server provided, will firstly install a new system by beaker.
    And will configure the satellite as virt-who testing requirements.
    Please refer to the README for usage.
    """
    logger.info("+++ Start to deploy the Satellite +++")

    satellite = args.satellite.split('-')
    args.version = satellite[0]
    args.repo = satellite[1]
    args.rhel_compose = rhel_compose_for_satellite(satellite[2])
    args.manifest = config.satellite.manifest

    # Install a new system by beaker when no server provided.
    if not args.server:
        beaker_args_define(args)
        args.server = install_rhel_by_beaker(args)
        args.ssh_username = config.beaker.default_username
        args.ssh_password = config.beaker.default_password
    ssh_satellite = SSHConnect(host=args.server,
                               user=args.ssh_username,
                               pwd=args.ssh_password)

    # Start to deploy and configure the satellite server
    satellite_deploy(args)
    satellite_settings(ssh_satellite, 'failed_login_attempts_limit', '0')
    satellite_settings(ssh_satellite, 'unregister_delete_host', 'true')

    # Update the [satellite] section of virtwho.ini
    config.update('satellite', 'server', args.server)
    config.update('satellite', 'username', args.admin_username)
    config.update('satellite', 'password', args.admin_password)
    config.update('satellite', 'ssh_username', args.ssh_username)
    config.update('satellite', 'ssh_password', args.ssh_password)

    # Create the organization and activation key as requirement.
    satellite = Satellite()
    second_org = config.satellite.secondary_org
    if second_org:
        satellite.org_create(name=second_org, label=second_org)
    activation_key = config.satellite.activation_key
    if activation_key:
        satellite.activation_key_create(key=activation_key)

    logger.info(f"+++ Succeeded to deploy the Satellite "
                f"{args.satellite}/{args.server} +++")
Пример #11
0
def create_rhel_container_by_docker(args):
    """
    Create rhel docker container
    """
    ssh_docker = SSHConnect(host=args.docker_server,
                            user=args.docker_username,
                            pwd=args.docker_password)

    # clean docker cache
    ssh_docker.runcmd('docker system prune -f')

    # Check if the container name already exists
    image_name = args.rhel_compose.lower()
    container_port = (args.container_port or docker_container_port(ssh_docker))
    container_name = (args.container_name
                      or docker_container_name(image_name, container_port))
    if docker_container_exist(ssh_docker, container_name):
        logger.warning(f'The docker container {container_name} already exists')
        return

    # copy docker files to docker server
    ssh_docker.runcmd('rm -rf /tmp/docker/;'
                      'rm -rf /tmp/mkimage*;'
                      'rm -f /etc/yum.repos.d/*.repo')
    local_dir = os.path.join(curPath, 'docker/')
    remote_dir = '/tmp/docker/'
    ssh_docker.put_dir(local_dir, remote_dir)

    # Create docker image and container
    docker_image_create(ssh_docker, image_name, args.rhel_compose)
    docker_container_create(ssh_docker, image_name, container_name,
                            container_port, args.container_username,
                            args.container_password)
    logger.info(f'Succeeded to create docker container:{container_name}, '
                f'port:{container_port}')
    return container_name, container_port
Пример #12
0
class Satellite:

    def __init__(self, org=None, activation_key=None):
        """
        Using hammer command to set satellite, handle organization and
        activation key, attach/remove subscription for host.
        Using api to check the host-to-guest associations.
        :param org: organization label, use the default_org configured
            in virtwho.ini as default.
        :param activation_key: activation key name, use the configure
            in virtwho.ini as default.
        """
        register = get_register_handler('satellite')
        self.org = org or register.default_org
        self.activation_key = activation_key or register.activation_key
        self.ssh = SSHConnect(host=register.server,
                              user=register.ssh_username,
                              pwd=register.ssh_password)
        self.hammer = 'hammer --output=json'
        self.org_id = self.organization_id()
        self.api = f'https://{register.server}'
        self.auth = (register.username, register.password)

    def organization_id(self, org=None):
        """
        Get the organization id by organization label.
        :param org: organization label, use the org when instantiate
        the class as default
        :return: organization id
        """
        org = org or self.org
        ret, output = self.ssh.runcmd(f'{self.hammer} organization info '
                                      f'--label "{org}" '
                                      f'--fields Id')
        output = json.loads(output)
        if ret == 0 and output:
            return output['Id']
        raise FailException(f'Failed to get the organization id for {org}')

    def org_create(self, name, label, description=None):
        """
        Create a new organization.
        :param name: the name of the organization.
        :param label: the label of the organization.
        :param description: the description for the organization.
        :return: True or raise Fail
        """
        description = description or ''
        _, output = self.ssh.runcmd(f'hammer organization create '
                                    f'--name "{name}" '
                                    f'--label "{label}" '
                                    f'--description "{description}"')
        if 'Organization created' in output:
            logger.info(f'Succeeded to create organization:{name}')
            return True
        if (
                'Name has already been taken' in output
                and
                'Label has already been taken' in output
        ):
            logger.info(f'The organization:{name} already existed')
            return True
        raise FailException(f'Failed to create organization:{name}')

    def org_delete(self, label):
        """
        Delete an organization by organization label.
        :param label: organization label
        :return: True or raise Fail
        """
        _, output = self.ssh.runcmd(f'hammer organization delete '
                                    f'--label "{label}"')
        if '100%' in output:
            logger.info(f'Succeeded to delete organization:{label}')
            return True
        if 'organization not found' in output:
            logger.info(f'The organization:{label} does not exist already')
            return True
        raise FailException(f'Failed to delete organization:{label}')

    def host_id(self, host):
        """
        Get the host id by host name or uuid or hwuuid.
        :param host: host name/uuid/hwuuid
        :return: host id or None
        """
        ret, output = self.ssh.runcmd(f'{self.hammer} host list '
                                      f'--organization-id {self.org_id} '
                                      f'--search {host} '
                                      f'--fields Id')
        output = json.loads(output)
        if ret == 0 and len(output) == 1:
            id = output[0]['Id']
            logger.info(f'Succeeded to get the host id, {host}:{id}')
            return id
        logger.warning(f'Failed to get the host id for {host}')
        return None

    def host_delete(self, host):
        """
        Delete a host by host name or uuid or hwuuid.
        :param host: host name/uuid/hwuuid
        :return: True or raise Fail
        """
        host_id = self.host_id(host)
        _, output = self.ssh.runcmd(f'hammer host delete '
                                    f'--organization-id {self.org_id} '
                                    f'--id {host_id}')
        if (
                ('Host deleted' in output or 'host not found' in output)
                and
                not self.host_id(host)
        ):
            logger.info(f'Succeeded to delete {host} from satellite')
            return True
        raise FailException(f'Failed to Delete {host} from satellite')

    def subscription_id(self, pool):
        """
        Get the subscription id by pool id.
        :param pool: pool id.
        :return: subscription id.
        """
        ret, output = self.ssh.runcmd(f'{self.hammer} '
                                      f'subscription list '
                                      f'--organization-id {self.org_id}')
        output = json.loads(output)
        if ret == 0 and output:
            for item in output:
                if item['UUID'] == pool:
                    subscription_id = item['ID']
                    return subscription_id
        raise FailException(f'Failed to get the subscription id for {pool}')

    def attach(self, host, pool=None, quantity=1):
        """
        Attach or auto attach subscription for one host/hypervisor.
        :param host: host name/uuid/hwuuid
        :param pool: pool id, run auto attach when pool=None.
        :param quantity: the subscription quantity to attach.
        :return: True or raise Fail.
        """
        host_id = self.host_id(host)
        cmd = f'hammer host subscription auto-attach --host-id {host_id}'
        msg = 'Auto attached subscriptions to the host successfully'
        if pool:
            subscription_id = self.subscription_id(pool=pool)
            cmd = f'hammer host subscription attach ' \
                  f'--host-id {host_id} ' \
                  f'--subscription-id {subscription_id} ' \
                  f'--quantity {quantity}'
            msg = 'Subscription attached to the host successfully'
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0 and msg in output:
            logger.info(f'Succeeded to attach subscription for {host}')
            return True
        raise FailException(f'Failed to attach subscription for {host}')

    def unattach(self, host, pool, quantity=1):
        """
        Remove subscription from one host/hypervisor by pool id.
        :param host: host name/uuid/hwuuid
        :param pool: pool id
        :param quantity: the subscription quantity to remove.
        :return:
        """
        host_id = self.host_id(host)
        subscription_id = self.subscription_id(pool=pool)
        ret, output = self.ssh.runcmd(f'hammer host subscription remove '
                                      f'--host-id {host_id} '
                                      f'--subscription-id {subscription_id} '
                                      f'--quantity {quantity}')
        msg = 'Subscription removed from the host successfully'
        if ret == 0 and msg in output:
            logger.info(f'Succeeded to remove subscription for {host}')
            return True
        raise FailException(f'Failed to remove subscription for {host}')

    def activation_key_create(self,
                              key=None,
                              content_view='Default Organization View',
                              environment='Library'):
        """
        Create one activation key.
        :param key: activation key name.
        :param content_view: 'Default Organization View' as default.
        :param environment: 'Library' as default.
        :return: True or raise Fail.
        """
        key = key or self.activation_key
        _, output = self.ssh.runcmd(f'hammer activation-key create '
                                    f'--organization-id {self.org_id} '
                                    f'--name {key} '
                                    f'--lifecycle-environment {environment} '
                                    f'--content-view "{content_view}"')
        if 'Activation key created' in output:
            logger.info(f'Succeeded to create activation key:{key}')
            return True
        if 'Name has already been taken' in output:
            logger.info(f'Activation key:{key} already exists')
            return True
        raise FailException(f'Failed to create activation key:{key}')

    def activation_key_delete(self, key=None):
        """
        Delete an activation key by key name.
        :param key: activation key name.
        :return: True or raise Fail.
        """
        key = key or self.activation_key
        _, output = self.ssh.runcmd(f'hammer activation-key delete '
                                    f'--organization-id {self.org_id} '
                                    f'--name {key}')
        if 'Activation key deleted' in output:
            logger.info(f'Succeeded to delete activation key:{key}')
            return True
        if 'activation_key not found' in output:
            logger.info(f'Activation key:{key} was not found')
            return True
        raise FailException(f'Failed to delete activation key:{key}')

    def activation_key_update(self, key=None, auto_attach='yes'):
        """
        Update auto attach setting for an activation key.
        :param key: activation key name, default to use the key when
            instantiate the class.
        :param auto_attach: boolean, true/false, yes/no, 1/0.
        :return: True or raise Fail.
        """
        key = key or self.activation_key
        _, output = self.ssh.runcmd(f'hammer activation-key update '
                                    f'--organization-id {self.org_id} '
                                    f'--name {key} '
                                    f'--auto-attach {auto_attach}')
        if 'Activation key updated' in output:
            logger.info(f'Succeeded to update activation key:{key} with '
                        f'auto_attach:{auto_attach}')
            return True
        raise FailException(f'Failed to update auto-attach for '
                            f'activation key:{key}')

    def activation_key_attach(self, pool, quantity=None, key=None):
        """
        Add subscription for activation key.
        :param pool: pool id.
        :param quantity: the subscription quantity to add.
        :param key: activation key name, default to use the key when
        instantiate the class.
        :return: True or raise Fail.
        """
        key = key or self.activation_key
        subscription_id = self.subscription_id(pool=pool)
        cmd = f'hammer activation-key add-subscription ' \
              f'--organization-id {self.org_id} ' \
              f'--name {key} ' \
              f'--subscription-id {subscription_id}'
        if quantity:
            cmd += f' --quantity {quantity}'
        ret, output = self.ssh.runcmd(cmd)
        if 'Subscription added to activation key' in output:
            logger.info(f'Succeeded to attach subscription for '
                        f'activation key:{key}')
            return True
        raise FailException(f'Failed to attach subscription for '
                            f'activation key:{key}')

    def activation_key_unattach(self, pool, key=None):
        """
        Remove subscription from activation key.
        :param pool: pool id.
        :param key: activation key name, default to use the key when
        instantiate the class.
        :return: True or raise Fail.
        """
        key = key or self.activation_key
        subscription_id = self.subscription_id(pool=pool)
        cmd = f'hammer activation-key remove-subscription ' \
              f'--organization-id {self.org_id} ' \
              f'--name {key} ' \
              f'--subscription-id {subscription_id}'
        ret, output = self.ssh.runcmd(cmd)
        if 'Subscription removed from activation key' in output:
            logger.info(f'Succeeded to remove subscription for '
                        f'activation key:{key}')
            return True
        raise FailException(f'Failed to remove subscription for '
                            f'activation key:{key}')

    def settings(self, name, value):
        """
        Update the settings.
        :param name: such as unregister_delete_host.
        :param value: the value.
        :return: True or raise Fail.
        """
        ret, output = self.ssh.runcmd(f'hammer settings set '
                                      f'--name={name} '
                                      f'--value={value}')
        if ret == 0 and f'Setting [{name}] updated to' in output:
            logger.info(f'Succeeded to set {name}:{value} for satellite')
            return True
        raise FailException(f'Failed to set {name}:{value} for satellite')

    def associate(self, hypervisor, guest):
        """
        Check the hypervisor is associated with guest on web.
        :param guest: guest name
        :param hypervisor: hypervisor host name/uuid/hwuuid
        """
        host_id = self.host_id(host=hypervisor)
        guest_id = self.host_id(host=guest)
        if host_id and guest_id:
            # Find the guest in hypervisor page
            ret, output = request_get(url=f'{self.api}/api/v2/hosts/{host_id}',
                                      auth=self.auth)
            if guest.lower() in str(output):
                logger.info(
                    'Succeeded to find the associated guest in hypervisor page')
            else:
                raise FailException(
                    'Failed to find the associated guest in hypervisor page')
            # Find the hypervisor in guest page
            ret, output = request_get(url=f'{self.api}/api/v2/hosts/{guest_id}',
                                      auth=self.auth)
            if hypervisor.lower() in str(output):
                logger.info(
                    'Succeeded to find the associated hypervisor in guest page')
            else:
                raise FailException(
                    'Failed to find the associated hypervisor in guest page')
Пример #13
0
class SubscriptionManager:

    def __init__(self, host, username, password, port=22, register_type='rhsm',
                 org=None, activation_key=None):
        """
        Define the global variables.
        :param host: ip/hostname to run subscription-manager command.
        :param username: account username of the host
        :param password: password to access the host
        :param port: port to access the host
        :param register_type: rhsm/satellite, rhsm as default
        :param org: organization of the entitlement server
        :param activation_key: activation_key of the satellite server.
            Will register by activation_key when the value is set.
        """
        self.host = host
        self.register_type = register_type
        self.rhsm_conf = '/etc/rhsm/rhsm.conf'
        self.ssh = SSHConnect(host=host, user=username, pwd=password, port=port)
        register = get_register_handler(register_type)
        self.server = register.server
        self.username = register.username
        self.password = register.password
        self.port = register.port
        self.prefix = register.prefix
        self.org = org or register.default_org
        self.activation_key = activation_key
        if register_type != 'satellite':
            self.baseurl = register.baseurl

    def register(self):
        """
        Register host by subscription-manager command
        """
        self.unregister()
        cmd = f'subscription-manager register ' \
              f'--serverurl={self.server}:{self.port}{self.prefix} ' \
              f'--org={self.org} '
        if self.activation_key:
            cmd += f'--activationkey={self.activation_key} '
        else:
            cmd += f'--username={self.username} ' \
                   f'--password={self.password} '
        if self.register_type == 'satellite':
            self.satellite_cert_install()
        else:
            cmd += f'--baseurl={self.baseurl}'
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0 and 'The system has been registered' in output:
            logger.info(f'Succeeded to register host')
            return output
        else:
            raise FailException(f'Failed to register {self.host}')

    def unregister(self):
        """
        Unregister and clean host by subscription-manager.
        """
        ret, _ = self.ssh.runcmd('subscription-manager unregister;'
                                 'subscription-manager clean')
        if ret == 0:
            logger.info(f'Succeeded to unregister host')
        else:
            raise FailException(f'Failed to unregister {self.host}')

    def satellite_cert_install(self):
        """
        Install certificate when registering to satellite.
        """
        cmd = f'rpm -ihv http://{self.server}' \
              f'/pub/katello-ca-consumer-latest.noarch.rpm'
        ret, _ = self.ssh.runcmd(cmd)
        if ret != 0:
            raise FailException(
                f'Failed to install satellite certification for {self.host}')

    def attach(self, pool=None, quantity=None):
        """
        Attach subscription by Pool ID or --auto.
        :param pool: Pool ID, attach by --auto when pool=None
        :param quantity: subscription number to attach, default is auto.
        :return: tty output.
        """
        cmd = 'subscription-manager attach '
        if pool:
            cmd += f'--pool={pool} '
        if quantity:
            cmd += f'--quantity={quantity}'
        if not pool:
            cmd += f'--auto '
        self.refresh()
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0:
            logger.info(f'Succeeded to attach subscription for {self.host}')
            return output.strip()
        if '--auto' in cmd and 'Unable to find available' in output:
            logger.warning(
                f'Failed to attach subscription by auto for {self.host}.')
            return output.strip()
        if 'Multi-entitlement not supported' in output:
            logger.warning(output)
            return output.strip()
        else:
            raise FailException(f'Failed to attach subscription for {self.host}')

    def unattach(self, pool=None):
        """
        Remove subscription by Pool ID or remove all.
        :param pool: Pool ID, remove all when pool=None.
        """
        cmd = 'subscription-manager remove --all'
        if pool:
            cmd = f'subscription-manager remove --pool={pool}'
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0:
            logger.info(f'Succeeded to remove subscription for {self.host}')
        else:
            raise FailException(f'Failed to remove subscription for {self.host}')

    def available(self, sku_id, sku_type='Virtual'):
        """
        Search and analyze an available subscription by name and type.
        :param sku_id: sku id, such as RH00001
        :param sku_type: 'Physical' or 'Virtual'.
        :return: a dict with sku attributes.
        """
        cmd = f'subscription-manager list --av --all --matches={sku_id} |' \
              f'tail -n +4'
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0 and "Pool ID:" in output:
            skus = output.strip().split('\n\n')
            for sku in skus:
                sku_attr = self.attr_analyzer(sku)
                if 'system_type' in sku_attr.keys():
                    sku_attr['sku_type'] = sku_attr['system_type']
                else:
                    sku_attr['sku_type'] = sku_attr['entitlement_type']
                if sku_attr['sku_type'] == sku_type:
                    logger.info(f'Succeeded to find {sku_type}:{sku_id} '
                                f'in {self.host}')
                    if '(Temporary)' in sku_attr['subscription_type']:
                        sku_attr['temporary'] = True
                    else:
                        sku_attr['temporary'] = False
                    return sku_attr
        logger.warning(f'Failed to find {sku_type}:{sku_id}' in {self.host})
        return None

    def consumed(self, pool):
        """
        List and analyze the consumed subscription by Pool ID.
        :param pool: Pool ID for checking.
        :return: a dict with sku attributes.
        """
        self.refresh()
        ret, output = self.ssh.runcmd(f'subscription-manager list --co')
        if ret == 0:
            if (output is None or
                    'No consumed subscription pools were found' in output):
                logger.info(f'No consumed subscription found in {self.host}.')
                return None
            elif "Pool ID:" in output:
                sku_attrs = output.strip().split('\n\n')
                for attr in sku_attrs:
                    sku_attr = self.attr_analyzer(attr)
                    if sku_attr['pool_id'] == pool:
                        logger.info(f'Succeeded to get the consumed '
                                    f'subscription in {self.host}')
                        if '(Temporary)' in sku_attr['subscription_type']:
                            sku_attr['temporary'] = True
                        else:
                            sku_attr['temporary'] = False
                        return sku_attr
        logger.warning('Failed to get consumed subscriptions.')
        return None

    def installed(self):
        """
        List products which are currently installed on the system and
        analyse the result.
        """
        self.refresh()
        ret, output = self.ssh.runcmd(
            'subscription-manager list --installed | tail -n +4')
        if ret == 0 and output.strip() != '':
            install_attr = self.attr_analyzer(output)
            logger.info(
                f'Succeeded to list installed subscription for {self.host}')
            return install_attr
        raise FailException(
            f'Failed to list installed subscription for {self.host}')

    def repo(self, action, repo):
        """
        Enable/disable one or more repos.
        :param action: enable or disable
        :param repo: a string including one or more repos separated
            by comma, such as "repo1, repo2, repo3...".
        """
        repo_list = repo.split(',')
        cmd = 'subscription-manager repos '
        for item in repo_list:
            cmd += f'--{action}="{item.strip()}" '
        ret, output = self.ssh.runcmd(cmd)
        if ret == 0:
            logger.info(f'Succeeded to {action} repo: {repo}')
        else:
            raise FailException(f'Failed to {action} repo: {repo}')

    def refresh(self):
        """
        Refresh subscription by command 'subscription-manager refresh'.
        """
        for i in range(3):
            ret, output = self.ssh.runcmd('subscription-manager refresh')
            if ret == 0:
                logger.info(f'Succeeded to refresh subscription')
                return True
            logger.warning('Try again to refresh subscription after 180s...')
            time.sleep(180)
        raise FailException(f'Failed to refresh subscription for {self.host}')

    def attr_analyzer(self, attr):
        """
        Analyze the output attributes to a dict, like the output from
        command "subscription-manager list --in/co/av"
        :param attr: the output including several lines, which lines are
            {string}:{string} format.
        :return: a dict
        """
        attr_data = dict()
        attr = attr.strip().split('\n')
        for line in attr:
            if ':' not in line:
                continue
            line = line.split(':')
            key = line[0].strip().replace(' ', '_').lower()
            value = line[1].strip()
            attr_data[key] = value
        return attr_data

    def facts_create(self, key, value):
        """
        Create subscription facts to /etc/rhsm/facts/custom.facts.
        :param key: fact key
        :param value: fact value
        """
        option = f'{{"{key}":"{value}"}}'
        ret, output = self.ssh.runcmd(
            f"echo '{option}' > /etc/rhsm/facts/custom.facts ;"
            f"subscription-manager facts --update")
        if ret == 0 and 'Successfully updated' in output:
            time.sleep(60)
            ret, output = self.ssh.runcmd(
                f"subscription-manager facts --list |grep '{key}:'")
            if ret == 0 and key in output:
                actual_value = output.split(": ")[1].strip()
                if actual_value == value:
                    logger.info(
                        f'Succeeded to create custom facts with for {self.host}')
        else:
            raise FailException(
                f'Failed to create custom facts for {self.host}')

    def facts_remove(self):
        """
        Remove subscription facts.
        """
        ret, output = self.ssh.runcmd('rm -f /etc/rhsm/facts/custom.facts;'
                                      'subscription-manager facts --update')
        time.sleep(60)
        if ret == 0 and 'Successfully updated' in output:
            logger.info(f'Succeeded to remove custom.facts for {self.host}')
        else:
            raise FailException(
                f'Failed to remove custom.facts for {self.host}')