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 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}')
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})')
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')
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)
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 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')
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} +++")
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)
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} +++")
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
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')
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}')