def get_cluster_name(self, _id): matching_names = [x['name'] for x in self.list_clusters() if x['id'] == _id] if matching_names: return matching_names[0] else: error(f"Cluster {_id} not found") sys.exit(1)
def create_cluster(self, name, overrides={}): existing_ids = [x['id'] for x in self.list_clusters() if x['name'] == name] if existing_ids: error(f"Cluster {name} already there. Leaving") sys.exit(1) if name.endswith('-day2'): self.create_day2_cluster(name, overrides) return self.set_default_values(overrides) new_cluster_params = default_cluster_params new_cluster_params['name'] = name extra_overrides = {} allowed_parameters = self._allowed_parameters(models.ClusterCreateParams) for parameter in overrides: if parameter == 'network_type' and overrides[parameter] not in ['OpenShiftSDN', 'OVNKubernetes']: extra_overrides[parameter] = overrides[parameter] continue if parameter in ['cluster_networks', 'service_networks']: extra_overrides[parameter] = overrides[parameter] continue if parameter in allowed_parameters: new_cluster_params[parameter] = overrides[parameter] elif parameter not in ['api_ip', 'ingress_ip']: extra_overrides[parameter] = overrides[parameter] else: warning(f"Omitting parameter {parameter} at creation time") cluster_params = models.ClusterCreateParams(**new_cluster_params) self.client.v2_register_cluster(new_cluster_params=cluster_params) if extra_overrides: self.update_cluster(name, extra_overrides)
def get_infra_env_name(self, _id): matching_names = [x['name'] for x in self.list_infra_envs() if x['id'] == _id] if matching_names: return matching_names[0] else: error(f"Infraenv {_id} not found") sys.exit(1)
def get_cluster_id(self, name): matching_ids = [x['id'] for x in self.list_clusters() if x['name'] == name or x['id'] == name] if matching_ids: return matching_ids[0] else: error(f"Cluster {name} not found") sys.exit(1)
def get_infra_env_id(self, name): valid_names = [name, f'{name}_infra-env'] matching_ids = [x['id'] for x in self.list_infra_envs() if x['name'] in valid_names or x['id'] == name] if matching_ids: return matching_ids[0] else: error(f"Infraenv {name} not found") sys.exit(1)
def _expired_iso(self, iso_url): search = re.search(r".*&image_token=(.*)&type=.*", iso_url) if search is not None: encoded_token = search.group(1) token = str(base64.urlsafe_b64decode(encoded_token + '===')) expiration_date = int(re.search(r'.*"exp":(.*),"sub".*"', token).group(1)) if datetime.fromtimestamp(expiration_date) < datetime.now(): return True else: return False else: error("couldn't parse iso_url") sys.exit(1)
def set_default_infraenv_values(self, overrides): if 'cluster' in overrides: cluster_id = self.get_cluster_id(overrides['cluster']) overrides['cluster_id'] = cluster_id if 'minimal' in overrides: image_type = "minimal-iso" if overrides['minimal'] else 'full-iso' overrides['image_type'] = image_type static_network_config = overrides.get('static_network_config', []) if static_network_config: if isinstance(static_network_config, dict): static_network_config = [static_network_config] final_network_config = [] for entry in static_network_config: mac_interface_map = entry.get('mac_interface_map', []) for interface in entry['interfaces']: bond = True if interface.get('type', 'ethernet') == 'bond' else False if not mac_interface_map: if not bond: logical_nic_name, mac_address = interface['name'], interface['mac-address'] mac_interface_map.append({"mac_address": mac_address, "logical_nic_name": logical_nic_name}) else: error("Providing mac_interface_map is mandatory when setting bond") sys.exit(1) new_entry = {'network_yaml': yaml.dump(entry), 'mac_interface_map': mac_interface_map} final_network_config.append(models.HostStaticNetworkConfig(**new_entry)) static_network_config = final_network_config overrides['static_network_config'] = static_network_config if 'ssh_authorized_key' not in overrides: if 'ssh_public_key' in overrides: overrides['ssh_authorized_key'] = overrides['ssh_public_key'] else: pub_key = overrides.get('public_key', f"{os.environ['HOME']}/.ssh/id_rsa.pub") if os.path.exists(pub_key): overrides['ssh_authorized_key'] = open(pub_key).read().strip() else: error(f"Missing public key file {pub_key}") sys.exit(1) if 'ignition_config_override' not in overrides: iso_overrides = overrides.copy() iso_overrides['ignition_version'] = '3.1.0' ignition_config_override = self.set_disconnected_ignition_config_override(infra_env_id=None, overrides=iso_overrides) if ignition_config_override is not None: overrides['ignition_config_override'] = ignition_config_override if 'proxy' in overrides and isinstance(overrides['proxy'], str): proxy = overrides['proxy'] if not proxy.startswith('http'): proxy = f'http://{proxy}' overrides['proxy'] = {'http_proxy': proxy, 'https_proxy': proxy} if 'noproxy' in overrides: overrides['proxy']['no_proxy'] = overrides['noproxy']
def create_infra_env(self, name, overrides={}): existing_ids = [x['id'] for x in self.list_infra_envs() if x['name'] == name] if existing_ids: error(f"Infraenv {name} already there. Leaving") sys.exit(1) self.set_default_values(overrides) self.set_default_infraenv_values(overrides) new_infraenv_params = default_infraenv_params new_infraenv_params['name'] = name allowed_parameters = self._allowed_parameters(models.InfraEnvCreateParams) for parameter in overrides: if parameter in allowed_parameters: new_infraenv_params[parameter] = overrides[parameter] infraenv_create_params = models.InfraEnvCreateParams(**new_infraenv_params) self.client.register_infra_env(infraenv_create_params=infraenv_create_params)
def set_olm_operators(self, olm_operators_data): operatorsapi = api.OperatorsApi(api_client=self.api) supported_operators = operatorsapi.v2_list_supported_operators() olm_operators = [] for operator in olm_operators_data: if isinstance(operator, str): operator_name = operator elif isinstance(operator, dict) and 'name' in operator: operator_name = operator['name'] else: error(f"Invalid entry for olm_operator {operator}") sys.exit(1) if operator_name not in supported_operators: error(f"Incorrect olm_operator {operator_name}. Should be one of {supported_operators}") sys.exit(1) olm_operators.append({'name': operator_name}) return olm_operators
def set_machine_networks(self, cluster_id, machine_networks_data): machine_networks = [] for machine_network in machine_networks_data: if isinstance(machine_network, str): cidr = machine_network elif isinstance(machine_network, dict) and 'cidr' in machine_network: cidr = machine_network['cidr'] else: error(f"Invalid entry for machine_network {machine_network}") sys.exit(1) try: ip_network(cidr) except: error(f"Invalid cidr for machine_network {machine_network}") sys.exit(1) machine_networks.append({'cidr': cidr, 'cluster_id': cluster_id}) return machine_networks
def set_service_networks(self, cluster_id, service_networks_data): service_networks = [] for service_network in service_networks_data: if isinstance(service_network, str): cidr = service_network elif isinstance(service_network, dict) and 'cidr' in service_network: cidr = service_network['cidr'] else: error(f"Invalid entry for service_network {service_network}") sys.exit(1) try: ip_network(cidr) except: error(f"Invalid cidr for service_network {service_network}") sys.exit(1) service_networks.append({'cidr': cidr, 'cluster_id': cluster_id}) return service_networks
def __init__(self, url, token=None, offlinetoken=None): self.url = url self.config = Configuration() self.config.host = self.url + "/api/assisted-install" self.config.verify_ssl = False proxies = urllib.request.getproxies() if proxies: proxy = proxies.get('https') or proxies.get('http') if 'http' not in proxy: proxy = "http://" + proxy warning(f"Detected proxy env var without scheme, updating proxy to {proxy}") self.config.proxy = proxy aihome = f"{os.environ['HOME']}/.aicli" if not os.path.exists(aihome): os.mkdir(aihome) if url in ['https://api.openshift.com', 'https://api.stage.openshift.com']: if offlinetoken is None: if os.path.exists(f'{aihome}/offlinetoken.txt'): offlinetoken = open(f'{aihome}/offlinetoken.txt').read().strip() else: error(f"offlinetoken needs to be set to gather token for {url}") error("get it at https://cloud.redhat.com/openshift/token") if os.path.exists('/i_am_a_container'): error("use -e AI_OFFLINETOKEN=$AI_OFFLINETOKEN to expose it in container mode") sys.exit(1) if not os.path.exists(f'{aihome}/offlinetoken.txt'): with open(f'{aihome}/offlinetoken.txt', 'w') as f: f.write(offlinetoken) self.offlinetoken = offlinetoken self.token = token if os.path.exists(f'{aihome}/token.txt'): self.token = open(f'{aihome}/token.txt').read().strip() try: self.token = get_token(token=self.token, offlinetoken=self.offlinetoken) except Exception as e: error(f"Hit issue when trying to set token. Got {e}") if os.path.exists(f'{aihome}/offlinetoken.txt'): error("Removing offlinetoken file") os.remove(f'{aihome}/offlinetoken.txt') sys.exit(1) self.config.api_key['Authorization'] = self.token self.config.api_key_prefix['Authorization'] = 'Bearer' self.api = ApiClient(configuration=self.config) self.client = api.InstallerApi(api_client=self.api)
def update_installconfig(self, name, overrides={}): cluster_id = self.get_cluster_id(name) installconfig = {} if 'network_type' in overrides or 'sno_disk' in overrides: if 'network_type' in overrides: installconfig['networking'] = {'networkType': overrides['network_type']} if 'sno_disk' in overrides: sno_disk = overrides['sno_disk'] if '/dev' not in sno_disk: sno_disk = f'/dev/{sno_disk}' installconfig['BootstrapInPlace'] = {'InstallationDisk': sno_disk} else: installconfig = overrides.get('installconfig') if installconfig is None: error("installconfig is not set") sys.exit(1) if not isinstance(installconfig, dict): error("installconfig is not in correct format") sys.exit(1) self.client.v2_update_cluster_install_config(cluster_id, json.dumps(installconfig))
def set_cluster_networks(self, cluster_id, cluster_networks_data): cluster_networks = [] for cluster_network in cluster_networks_data: host_prefix = None if isinstance(cluster_network, str): cidr = cluster_network elif isinstance(cluster_network, dict) and 'cidr' in cluster_network: cidr = cluster_network['cidr'] host_prefix = cluster_network.get('host_prefix') or cluster_network.get('hostPrefix') else: error(f"Invalid entry for cluster_network {cluster_network}") sys.exit(1) try: ip_network(cidr) except: error(f"Invalid cidr for cluster_network {cluster_network}") sys.exit(1) if host_prefix is None: host_prefix = 64 if ':' in cidr else 23 cluster_networks.append({'cidr': cidr, 'cluster_id': cluster_id, 'host_prefix': host_prefix}) return cluster_networks
def delete_host(self, hostname, overrides={}): infra_envs = {} if 'infraenv' in overrides: infraenv = overrides['infraenv'] infra_env_id = self.get_infra_env_id(infraenv) hosts = self.client.v2_list_hosts(infra_env_id=infra_env_id) matchingids = [host['id'] for host in hosts if host['requested_hostname'] == hostname or host['id'] == hostname] else: for infra_env in self.client.list_infra_envs(): infra_env_id = infra_env['id'] hosts = self.client.v2_list_hosts(infra_env_id=infra_env_id) matchingids = [host['id'] for host in hosts if host['requested_hostname'] == hostname or host['id'] == hostname] if matchingids: infra_envs[infra_env_id] = matchingids if not infra_envs: error(f"No Matching Host with name {hostname} found") for infra_env_id in infra_envs: host_ids = infra_envs[infra_env_id] for host_id in host_ids: info(f"Deleting Host with id {host_id} in infraenv {infra_env_id}") self.client.v2_deregister_host(infra_env_id, host_id)
def set_default_values(self, overrides, existing=False): if 'openshift_version' in overrides and isinstance(overrides['openshift_version'], float): overrides['openshift_version'] = str(overrides['openshift_version']) if 'domain' in overrides: overrides['base_dns_domain'] = overrides['domain'] if not existing: if 'pull_secret' not in overrides: warning("Using openshift_pull.json as pull_secret file") overrides['pull_secret'] = "openshift_pull.json" pull_secret = os.path.expanduser(overrides['pull_secret']) if not os.path.exists(pull_secret): error(f"Missing pull secret file {pull_secret}") sys.exit(1) overrides['pull_secret'] = re.sub(r"\s", "", open(pull_secret).read()) if 'ssh_public_key' not in overrides: pub_key = overrides.get('public_key', f"{os.environ['HOME']}/.ssh/id_rsa.pub") if os.path.exists(pub_key): overrides['ssh_public_key'] = open(pub_key).read().strip() else: error(f"Missing public key file {pub_key}") sys.exit(1) if 'sno' in overrides: if overrides['sno']: overrides['high_availability_mode'] = "None" overrides['user_managed_networking'] = True if 'high_availability_mode' in overrides and overrides['high_availability_mode'] is None: overrides['high_availability_mode'] = "None" if 'olm_operators' in overrides: overrides['olm_operators'] = self.set_olm_operators(overrides['olm_operators']) if 'tpm' in overrides and overrides['tpm']: overrides['disk_encryption'] = {"enable_on": "all", "mode": "tpmv2"} if 'tang_servers' in overrides: tang_servers = overrides['tang_servers'] if isinstance(tang_servers, list): tang_servers = ','.join(tang_servers) overrides['disk_encryption'] = {"enable_on": "all", "mode": "tpmv2", "tang_servers": tang_servers}
def upload_manifests(self, name, directory, openshift=False): cluster_id = self.get_cluster_id(name) if not os.path.exists(directory): error(f"Directory {directory} not found") sys.exit(1) elif not os.path.isdir(directory): error(f"{directory} is not a directory") sys.exit(1) manifests_api = api.ManifestsApi(api_client=self.api) _fics = os.listdir(directory) if not _fics: error(f"No files found in directory {directory}") sys.exit(0) for _fic in _fics: if not _fic.endswith('.yml') and not _fic.endswith('.yaml'): warning(f"skipping file {_fic}") continue info(f"uploading file {_fic}") content = base64.b64encode(open(f"{directory}/{_fic}").read().encode()).decode("UTF-8") folder = 'manifests' if not openshift else 'openshift' manifest_info = {'file_name': _fic, 'content': content, 'folder': folder} create_manifest_params = models.CreateManifestParams(**manifest_info) manifests_api.v2_create_cluster_manifest(cluster_id, create_manifest_params)
def update_cluster(self, name, overrides): cluster_id = self.get_cluster_id(name) if 'api_ip' in overrides: overrides['api_vip'] = overrides['api_ip'] if 'ingress_ip' in overrides: overrides['ingress_vip'] = overrides['ingress_ip'] if 'pull_secret' in overrides: pull_secret = os.path.expanduser(overrides['pull_secret']) if os.path.exists(pull_secret): overrides['pull_secret'] = re.sub(r"\s", "", open(pull_secret).read()) else: warning("Using pull_secret as string") if 'role' in overrides: role = overrides['role'] hosts_roles = [{"id": host['id'], "role": role} for host in self.client.list_hosts(cluster_id=cluster_id)] overrides['hosts_roles'] = hosts_roles installconfig = {} if 'network_type' in overrides: installconfig['networking'] = {'networkType': overrides['network_type']} del overrides['network_type'] if 'sno_disk' in overrides: sno_disk = overrides['sno_disk'] if '/dev' not in sno_disk: sno_disk = f'/dev/{sno_disk}' installconfig['BootstrapInPlace'] = {'InstallationDisk': sno_disk} if 'tpm' in overrides and overrides['tpm']: if overrides.get('tpm_masters', False): enable_on = 'masters' elif overrides.get('tpm_workers', False): enable_on = 'masters' else: enable_on = 'all' installconfig['disk_encryption'] = {"enable_on": enable_on, "mode": "tpmv2"} if 'tang_servers' in overrides and isinstance(overrides['tang_servers'], list): if overrides.get('tpm_masters', False): enable_on = 'masters' elif overrides.get('tpm_workers', False): enable_on = 'masters' else: enable_on = 'all' tang_servers = overrides['tang_servers'] installconfig['disk_encryption'] = {"enable_on": "all", "mode": "tpmv2", "tang_servers": tang_servers} if 'proxy' in overrides: proxy = overrides['proxy'] if isinstance(proxy, str): httpProxy, httpsProxy = proxy, proxy noproxy = overrides.get('noproxy') elif isinstance(proxy, dict): httpProxy = proxy.get('http_proxy') or proxy.get('httpProxy') httpsProxy = proxy.get('https_proxy') or proxy.get('httpsProxy') noproxy = proxy.get('no_proxy') or proxy.get('noProxy') else: error(f"Invalid entry for proxy: {proxy}") sys.exit(1) if not httpProxy.startswith('http'): httpProxy = f'http://{httpProxy}' if not httpsProxy.startswith('http'): httpsProxy = f'http://{httpsProxy}' installconfig['proxy'] = {'httpProxy': httpProxy, 'httpsProxy': httpsProxy, 'noProxy': noproxy} if 'installconfig' in overrides: installconfig = overrides['installconfig'] if installconfig: self.client.v2_update_cluster_install_config(cluster_id, json.dumps(installconfig)) if 'olm_operators' in overrides: overrides['olm_operators'] = self.set_olm_operators(overrides['olm_operators']) if 'machine_networks' in overrides: overrides['machine_networks'] = self.set_machine_networks(cluster_id, overrides['machine_networks']) if 'service_networks' in overrides: overrides['service_networks'] = self.set_service_networks(cluster_id, overrides['service_networks']) if 'cluster_networks' in overrides: overrides['cluster_networks'] = self.set_cluster_networks(cluster_id, overrides['cluster_networks']) if overrides: cluster_update_params = overrides.copy() allowed_parameters = self._allowed_parameters(models.V2ClusterUpdateParams) for parameter in overrides: if parameter not in allowed_parameters: del cluster_update_params[parameter] cluster_update_params = models.V2ClusterUpdateParams(**cluster_update_params) self.client.v2_update_cluster(cluster_id=cluster_id, cluster_update_params=cluster_update_params)
def update_host(self, hostname, overrides): infra_envs = {} if 'infraenv' in overrides: infra_env = overrides['infraenv'] infra_env_id = self.get_infra_env_id(infra_env) hosts = self.client.v2_list_hosts(infra_env_id=infra_env_id) matchingids = [host['id'] for host in hosts if host['requested_hostname'] == hostname or host['id'] == hostname] else: for infra_env in self.client.list_infra_envs(): infra_env_id = infra_env['id'] hosts = self.client.v2_list_hosts(infra_env_id=infra_env_id) matchingids = [host['id'] for host in hosts if host['requested_hostname'] == hostname or host['id'] == hostname] if matchingids: infra_envs[infra_env_id] = matchingids if not infra_envs: error(f"No Matching Host with name {hostname} found") for infra_env_id in infra_envs: host_ids = infra_envs[infra_env_id] for index, host_id in enumerate(host_ids): role = None bind_updated = False host_update_params = {} if 'cluster' in overrides: cluster = overrides['cluster'] if cluster is None or cluster == '': self.client.unbind_host(infra_env_id=infra_env_id, host_id=host_id) else: cluster_id = self.get_cluster_id(cluster) bind_host_params = {'cluster_id': cluster_id} bind_host_params = models.BindHostParams(**bind_host_params) self.client.bind_host(infra_env_id, host_id, bind_host_params) bind_updated = True if 'role' in overrides: role = overrides['role'] host_update_params['host_role'] = role if 'name' in overrides or 'requested_hostname' in overrides: newname = overrides.get('name', overrides.get('requested_hostname')) if len(host_ids) > 1: newname = f"{newname}-{index}" host_update_params['host_name'] = newname if 'ignition' in overrides: ignition_path = overrides['ignition'] if not os.path.exists(ignition_path): warning(f"Ignition {ignition_path} not found. Ignoring") else: ignition_data = open(ignition_path).read() host_ignition_params = models.HostIgnitionParams(config=ignition_data) self.client.v2_update_host_ignition(infra_env_id, host_id, host_ignition_params) if 'extra_args' in overrides and cluster_id is not None: extra_args = overrides['extra_args'] installer_args_params = models.InstallerArgsParams(args=extra_args) self.client.v2_update_host_installer_args(cluster_id, host_id, installer_args_params) if 'mcp' in overrides: valid_status = ["discovering", "known", "disconnected", "insufficient", "pending-for-input"] currenthost = self.client.v2_get_host(infra_env_id=infra_env_id, host_id=host_id) currentstatus = currenthost.status if currentstatus not in valid_status: error(f"Mcp can't be set for host {hostname} because of incorrect status {currentstatus}") else: mcp = overrides['mcp'] host_update_params['machine_config_pool_name'] = mcp if host_update_params: info(f"Updating host with id {host_id}") host_update_params = models.HostUpdateParams(**host_update_params) self.client.v2_update_host(infra_env_id=infra_env_id, host_id=host_id, host_update_params=host_update_params) elif not bind_updated: warning("Nothing updated for this host")