From ebfec1b561e6fa50dce904aafdc687fcf1509fcb Mon Sep 17 00:00:00 2001 From: Dharaneeshwaran Ravichandran Date: Thu, 16 Oct 2025 11:01:55 +0530 Subject: [PATCH] Bug fix on destroy flow and logging improvements Add request handling exceptions Signed-off-by: Dharaneeshwaran Ravichandran --- cli/auth/auth.py | 43 +++++---- cli/cmd/destroy.py | 25 ++++-- cli/cmd/launch.py | 14 +-- cli/cmd/rollback.py | 1 - cli/cmd/status.py | 4 +- cli/cmd/upgrade.py | 1 - cli/network/virtual_network.py | 83 +++++++++-------- cli/partition/activation.py | 128 +++++++++++++++------------ cli/partition/partition.py | 157 ++++++++++++++++----------------- cli/storage/storage.py | 42 ++++----- cli/storage/virtual_storage.py | 51 +++++------ cli/storage/vopt_storage.py | 24 ++--- cli/utils/command_util.py | 56 ++++++------ cli/utils/common.py | 15 +--- cli/utils/iso_util.py | 63 +++++-------- cli/utils/monitor_util.py | 10 +-- cli/utils/validator.py | 38 ++++---- cli/vios/vios.py | 101 +++++++++++---------- docs/deployer-guide.md | 2 +- examples/hmc-agent/app/hmc.py | 2 +- install_linux.sh | 2 +- 21 files changed, 426 insertions(+), 436 deletions(-) diff --git a/cli/auth/auth.py b/cli/auth/auth.py index 191d0b4..e2f910f 100644 --- a/cli/auth/auth.py +++ b/cli/auth/auth.py @@ -25,25 +25,32 @@ def populate_payload(config): ''' def authenticate_hmc(config): - # Populate Authentication payload - payload = populate_payload(config) - url = "https://" + util.get_host_address(config) + URI - headers = {"Content-Type": CONTENT_TYPE, "Accept": ACCEPT} - response = requests.put(url, headers=headers, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to authenticate HMC, error: {response.text}") - raise AuthError(f"failed to authenticate HMC, error: {response.text}") - - soup = BeautifulSoup(response.text, 'xml') - session_key = soup.find("X-API-Session") + try: + # Populate Authentication payload + payload = populate_payload(config) + url = "https://" + util.get_host_address(config) + URI + headers = {"Content-Type": CONTENT_TYPE, "Accept": ACCEPT} + response = requests.put(url, headers=headers, data=payload, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + session_key = soup.find("X-API-Session") + except requests.exceptions.RequestException as e: + raise AuthError(f"failed to authenticate HMC while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise AuthError(f"failed to authenticate HMC, error: {e}") return session_key.text, response.cookies def delete_session(config, cookies): - url = "https://" + util.get_host_address(config) + URI - headers = {"x-api-key": util.get_session_key(config)} - response = requests.delete(url, cookies=cookies, headers=headers, verify=False) - if response.status_code != 204: - logger.error(f"failed to delete session on HMC, error: {response.text}") - raise AuthError(f"failed to delete session on HMC, error: {response.text}") - logger.debug("Logged off HMC session successfully") + try: + url = "https://" + util.get_host_address(config) + URI + headers = {"x-api-key": util.get_session_key(config)} + response = requests.delete(url, cookies=cookies, headers=headers, verify=False) + response.raise_for_status() + + logger.debug("Logged off HMC session successfully") + except requests.exceptions.RequestException as e: + raise AuthError(f"failed to delete session on HMC while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise AuthError(f"failed to delete session on HMC, error: {e}") return diff --git a/cli/cmd/destroy.py b/cli/cmd/destroy.py index 93a9d28..3fe1f02 100644 --- a/cli/cmd/destroy.py +++ b/cli/cmd/destroy.py @@ -16,8 +16,9 @@ def destroy(config_file_path): # Invoking initialize_command to perform common actions like validation, authentication etc. is_config_valid, cookies, sys_uuid, vios_uuid_list = command_util.initialize_command(config) if is_config_valid: - _destroy(config, cookies, sys_uuid, vios_uuid_list) - logger.info("PIM partition successfully destroyed") + destroyed = _destroy(config, cookies, sys_uuid, vios_uuid_list) + if destroyed: + logger.info("PIM partition successfully destroyed") except (Exception) as e: logger.error(f"encountered an error: {e}") finally: @@ -25,25 +26,31 @@ def destroy(config_file_path): command_util.cleanup(config, cookies) def _destroy(config, cookies, sys_uuid, vios_uuid_list): + destroy_succeded = False try: exists, created_by_pim, partition_uuid = partition.check_partition_exists( config, cookies, sys_uuid) if exists: logger.info("Shutting down the partition") - activation.shutdown_partition(config, cookies, partition_uuid) - logger.info("Partition shut down") + shutdown = activation.shutdown_partition(config, cookies, partition_uuid) + if shutdown: + logger.info("Partition shut down") logger.info( "Detaching installation medias and physical disk from the partition") - vios_operation.cleanup_vios( + vios_cleanup = vios_operation.cleanup_vios( config, cookies, sys_uuid, partition_uuid, vios_uuid_list) - logger.info( - "Detached installation medias and physical disk from the partition") + if vios_cleanup: + logger.info( + "Detached installation medias and physical disk from the partition") if created_by_pim and exists: logger.info("Destroying the partition") - partition.remove_partition(config, cookies, partition_uuid) + destroyed = partition.remove_partition(config, cookies, partition_uuid) logger.info("Partition destroyed") + + # Decide destroy_succeded or not via all the operation's status + destroy_succeded = shutdown and vios_cleanup and destroyed except Exception as e: raise e - return + return destroy_succeded diff --git a/cli/cmd/launch.py b/cli/cmd/launch.py index 5515a5e..b66c9e3 100644 --- a/cli/cmd/launch.py +++ b/cli/cmd/launch.py @@ -51,15 +51,13 @@ def _launch(config, cookies, sys_uuid, vios_uuids): active_vios_servers = vios_operation.get_active_vios( config, cookies, sys_uuid, vios_uuids) if len(active_vios_servers) == 0: - logger.error("failed to find active VIOS server") - raise VIOSError("failed to find active VIOS server") + raise VIOSError(f"no active VIOS server attached to the system '{util.get_host_address(config)}'") logger.debug( f"List of active VIOS '{list(active_vios_servers.keys())}'") vios_media_uuid_list = vios_operation.get_vios_with_mediarepo_tag(active_vios_servers) if len(vios_media_uuid_list) == 0: - logger.error("failed to find VIOS server for the partition") - raise StorageError("failed to find VIOS server for the partition") + raise StorageError(f"no VIOS server attached with media repository on the system '{util.get_host_address(config)}'") logger.info("Setting up partition") exists, created_by_pim, partition_uuid = partition.check_partition_exists(config, cookies, sys_uuid) @@ -186,10 +184,7 @@ def setup_storage(config, cookies, active_vios, sys_uuid, lpar_id): vios_storage_list = vios_operation.get_vios_with_physical_storage( config, active_vios) if len(vios_storage_list) == 0: - logger.error( - "failed to find physical volume for the partition") - raise StorageError( - "failed to find physical volume for the partition") + raise StorageError(f"no VIOS server attached with available physical disk on the system '{util.get_host_address(config)}' to attach to the PIM partition") storage.attach_physical_storage( config, cookies, sys_uuid, lpar_id, vios_storage_list) except (StorageError, VIOSError, Exception) as e: @@ -200,8 +195,7 @@ def handle_virtual_disk(config, cookies, active_vios, sys_uuid, lpar_id): vios_storage_list = vios_operation.get_vios_with_physical_storage( config, active_vios) if len(vios_storage_list) == 0: - logger.error("failed to find physical volume for the partition") - raise StorageError("failed to find physical volume for the partition") + raise StorageError(f"no VIOS server attached with available physical disk on the system '{util.get_host_address(config)}' to attach to the PIM partition") vios_storage_uuid = vios_storage_list[0][0] updated_vios_payload = vios_operation.get_vios_details(config, cookies, sys_uuid, vios_storage_uuid) diff --git a/cli/cmd/rollback.py b/cli/cmd/rollback.py index 03e9bdf..fc9429a 100644 --- a/cli/cmd/rollback.py +++ b/cli/cmd/rollback.py @@ -57,5 +57,4 @@ def _rollback(config): return False return True except Exception as e: - logger.error(f"failed to rollback PIM partition, error: {e}") raise Exception(f"failed to rollback PIM partition, error: {e}") diff --git a/cli/cmd/status.py b/cli/cmd/status.py index 0842987..740bab7 100644 --- a/cli/cmd/status.py +++ b/cli/cmd/status.py @@ -30,12 +30,12 @@ def _status(config, cookies, sys_uuid): logger.debug("Checking partition exists") exists, _, partition_uuid = partition.check_partition_exists(config, cookies, sys_uuid) if not exists: - logger.error(f"Partition named '{util.get_partition_name(config)}' not found") + logger.error(f"partition named '{util.get_partition_name(config)}' not found") return lpar_state = activation.check_lpar_status(config, cookies, partition_uuid) if lpar_state != "running": - logger.error(f"Partition '{util.get_partition_name(config)}' not in running state") + logger.error(f"partition '{util.get_partition_name(config)}' not in running state") return logger.info(f"PIM partition '{util.get_partition_name(config)}' is in running state") diff --git a/cli/cmd/upgrade.py b/cli/cmd/upgrade.py index d416bcd..252d42e 100644 --- a/cli/cmd/upgrade.py +++ b/cli/cmd/upgrade.py @@ -80,7 +80,6 @@ def _upgrade(config): logger.info("Monitoring boot process, this will take a while") monitor_util.monitor_pim(config) except Exception as e: - logger.error(f"failed to upgrade PIM partition, error: {e}") raise Exception(f"failed to upgrade PIM partition, error: {e}") return upgraded diff --git a/cli/network/virtual_network.py b/cli/network/virtual_network.py index 1b0fef1..b7844f2 100644 --- a/cli/network/virtual_network.py +++ b/cli/network/virtual_network.py @@ -28,41 +28,46 @@ def populate_payload(vlanid, vswitchid, vswitchname): ''' def get_network_uuid(config, cookies, system_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualNetwork/quick/All" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml, type=VirtualNetwork"} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to list VLAN, error: {response.text}") - raise NetworkError(f"failed to list VLAN, error: {response.text}") - uuid = "" - network_name = util.get_vnetwork_name(config) - for nw in response.json(): - if nw["NetworkName"] == network_name: - uuid = nw["UUID"] - break + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualNetwork/quick/All" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml, type=VirtualNetwork"} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + + uuid = "" + network_name = util.get_vnetwork_name(config) + for nw in response.json(): + if nw["NetworkName"] == network_name: + uuid = nw["UUID"] + break - if "" == uuid: - logger.error(f"failed to find virtual network with name '{network_name}'") - raise NetworkError(f"failed to find virtual network with name '{network_name}'") - else: - logger.debug(f"Network UUID for the virtual network {network_name}: {uuid}") + if "" == uuid: + raise NetworkError(f"no virtual network available with name '{network_name}'") + else: + logger.debug(f"Network UUID for the virtual network {network_name}: {uuid}") + except requests.exceptions.RequestException as e: + raise NetworkError(f"failed to list VLAN while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise NetworkError(f"failed to list VLAN, error: {e}") return uuid def get_vlan_details(config, cookies, system_uuid): - nw_uuid = get_network_uuid(config, cookies, system_uuid) - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualNetwork/{nw_uuid}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml, type=VirtualNetwork"} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get VLAN details, error: {response.text}") - raise NetworkError(f"failed to get VLAN details, error: {response.text}") - - soup = BeautifulSoup(response.text, 'xml') - vlan_id = soup.find("NetworkVLANID") - vswitch_id = soup.find("VswitchID") - + try: + nw_uuid = get_network_uuid(config, cookies, system_uuid) + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualNetwork/{nw_uuid}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml, type=VirtualNetwork"} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + vlan_id = soup.find("NetworkVLANID") + vswitch_id = soup.find("VswitchID") + except requests.exceptions.RequestException as e: + raise NetworkError(f"failed to get VLAN details while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise NetworkError(f"failed to get VLAN details, error: {e}") return vlan_id.text, vswitch_id.text def check_network_adapter(config, cookies, partition_uuid, vlan_id, vswitch_id): @@ -81,11 +86,11 @@ def check_network_adapter(config, cookies, partition_uuid, vlan_id, vswitch_id): logger.debug(f"Found network with VLAN '{vlan_id}' and Switch '{vswitch_id}' attached to lpar.") slot_num = soup.find("VirtualSlotNumber").text return True, slot_num - else: - raise NetworkError(f"failed to check if virtual network is attached to the partition, error: {response.text}") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise NetworkError(f"failed to check if virtual network is attached to the partition while making http request, error: {e}, response: {e.response.text}") except Exception as e: - logger.error(f"failed to check if virtual network is attached to the partition, error: {e}") - raise e + raise NetworkError(f"failed to check if virtual network is attached to the partition, error: {e}") return False, slot_num def attach_network(config, cookies, system_uuid, partition_uuid): @@ -105,10 +110,10 @@ def attach_network(config, cookies, system_uuid, partition_uuid): url = "https://" + util.get_host_address(config) + uri headers = {"x-api-key": util.get_session_key(config), "Content-Type": CONTENT_TYPE} response = requests.put(url, headers=headers, cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to attach virtual network to the partition, error: {response.text}") - raise NetworkError(f"failed to attach virtual network to the partition, error: {response.text}") + response.raise_for_status() logger.debug(f"Network '{util.get_vnetwork_name(config)}' attached to lpar") + except requests.exceptions.RequestException as e: + raise NetworkError(f"failed to attach virtual network to the partition while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise NetworkError(f"failed to attach virtual network to the partition, error: {e}") return DEFAULT_NW_SLOT diff --git a/cli/partition/activation.py b/cli/partition/activation.py index ae42662..bfb4770 100644 --- a/cli/partition/activation.py +++ b/cli/partition/activation.py @@ -111,28 +111,37 @@ def shutdown_payload(): """ def get_lpar_profile_id(config, cookies, partition_uuid): - uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}/LogicalPartitionProfile" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartitionProfile"} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get LPAR profile ID, error: {response.text}") - raise PartitionError(f"failed to get LPAR profile ID, error: {response.text}") - soup = BeautifulSoup(response.text, 'xml') - entry_node = soup.find('entry') - lpar_profile_id = entry_node.find('id') + try: + uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}/LogicalPartitionProfile" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartitionProfile"} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + entry_node = soup.find('entry') + lpar_profile_id = entry_node.find('id') + except requests.exceptions.RequestException as e: + raise PartitionError(f"ffailed to get LPAR profile ID while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to get LPAR profile ID, error: {e}") return lpar_profile_id.text def poll_job_status(config, cookies, job_id): - uri = f"/rest/api/uom/jobs/{job_id}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.web+xml; type=JobRequest"} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get job completion, error: {response.text}") - raise PartitionError(f"failed to get job completion, error: {response.text}") - soup = BeautifulSoup(response.text, 'xml') - status = soup.find("Status").text + try: + uri = f"/rest/api/uom/jobs/{job_id}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.web+xml; type=JobRequest"} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + status = soup.find("Status").text + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to get job completion while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to get job completion, error: {e}") + if status == "COMPLETED_OK": return True else: @@ -158,50 +167,55 @@ def check_job_status(config, cookies, response): return False def activate_partition(config, cookies, partition_uuid): - # Check partition state,don't activate if its in 'running' state - lpar_state = check_lpar_status(config, cookies, partition_uuid) - if lpar_state == "running": - logger.debug("Partition already in 'running' state, skipping activation") - return - - uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}/do/PowerOn" - url = "https://" + util.get_host_address(config) + uri - lpar_profile_id = get_lpar_profile_id(config, cookies, partition_uuid) - payload = populated_payload(lpar_profile_id) - - headers = {"x-api-key": util.get_session_key(config), "Content-Type": CONTENT_TYPE} - response = requests.put(url, headers=headers, cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to activate partition, error: {response.text}") - raise PartitionError(f"failed to activate partition, error: {response.text}") - # check job status for COMPLETED_OK - status = check_job_status(config, cookies, response.text) - if not status: - logger.error(f"failed to activate partition, activate job returned false") - raise PartitionError(f"failed to activate partition, activate job returned false") - logger.debug("Partition activated successfully.") + try: + # Check partition state,don't activate if its in 'running' state + lpar_state = check_lpar_status(config, cookies, partition_uuid) + if lpar_state == "running": + logger.debug("Partition already in 'running' state, skipping activation") + return + + uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}/do/PowerOn" + url = "https://" + util.get_host_address(config) + uri + lpar_profile_id = get_lpar_profile_id(config, cookies, partition_uuid) + payload = populated_payload(lpar_profile_id) + + headers = {"x-api-key": util.get_session_key(config), "Content-Type": CONTENT_TYPE} + response = requests.put(url, headers=headers, cookies=cookies, data=payload, verify=False) + response.raise_for_status() + + # check job status for COMPLETED_OK + status = check_job_status(config, cookies, response.text) + if not status: + raise PartitionError(f"activate job returned false") + logger.debug("Partition activated successfully.") + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to activate partition while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to activate partition, error: {e}") return def check_lpar_status(config, cookies, partition_uuid): - uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config)} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get LPAR details for '{partition_uuid}', error: {response.text}") - raise PartitionError(f'Failed to get LPAR details for {partition_uuid}') - soup = BeautifulSoup(response.text, 'xml') - state = soup.find("PartitionState") - if state == None: - logger.error(f"partition state of LPAR '{partition_uuid}' found to be None") - raise PartitionError(f'Failed to get LPAR status for {partition_uuid}') + try: + uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config)} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + soup = BeautifulSoup(response.text, 'xml') + state = soup.find("PartitionState") + if state == None: + raise PartitionError(f"partition state of LPAR '{partition_uuid}' found to be None") + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to get LPAR details for '{partition_uuid}' while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to get LPAR details for '{partition_uuid}', error: {e}") return state.text def shutdown_partition(config, cookies, partition_uuid): lpar_state = check_lpar_status(config, cookies, partition_uuid) if lpar_state == "not activated": logger.debug("Partition already in 'not activated' state, skipping shutdown") - return + return True uri = f"/rest/api/uom/LogicalPartition/{partition_uuid}/do/PowerOff" url = "https://" + util.get_host_address(config) + uri @@ -210,10 +224,10 @@ def shutdown_partition(config, cookies, partition_uuid): response = requests.put(url, headers=headers, cookies=cookies, data=payload, verify=False) if response.status_code != 200: logger.error(f"failed to shutdown partition, error: {response.text}") - return + return False # check job status for COMPLETED_OK status = check_job_status(config, cookies, response.text) if not status: logger.error(f"failed to shutdown partition, shutdown job returned false") - return - return + return False + return True diff --git a/cli/partition/partition.py b/cli/partition/partition.py index 2c4f3d3..09f2dce 100644 --- a/cli/partition/partition.py +++ b/cli/partition/partition.py @@ -84,10 +84,8 @@ def get_lpar_update_payload(config, curr_lpar_payload): max_memory = lpar_mem_config.find("MaximumMemory") desired_memory = lpar_mem_config.find("DesiredMemory") if min_memory is None or max_memory is None or desired_memory is None: - logger.error( - "XML parsing error: Unable to find memory configs from partition payload") - raise Exception( - "XML parsing error: Unable to find memory configs from partition payload") + raise PartitionError( + "xml parsing error: unable to find memory configs from partition payload") min_memory.string.replace_with( str(convert_gb_to_mb(util.get_min_memory(config)))) max_memory.string.replace_with( @@ -98,36 +96,27 @@ def get_lpar_update_payload(config, curr_lpar_payload): lpar_cpu_config = curr_lpar.find("PartitionProcessorConfiguration") partition_name = curr_lpar.find("PartitionName") if util.has_dedicated_proc(config) == "true": - logger.debug("update-compute: dedicated proc mode") desired_proc = lpar_cpu_config.find("DesiredProcessors") min_proc = lpar_cpu_config.find("MinimumProcessors") max_proc = lpar_cpu_config.find("MaximumProcessors") if desired_proc is None or min_proc is None or max_proc is None: - logger.error( - "XML parsing error: Unable to find processor configs from partition payload") - raise Exception( - "XML parsing error: Unable to find processor configs from partition payload") + raise PartitionError( + "xml parsing error: unable to find processor configs from partition payload") desired_proc.string.replace_with(util.get_desired_proc(config)) min_proc.string.replace_with(util.get_min_proc(config)) max_proc.string.replace_with(util.get_max_proc(config)) else: # Switch from dedicated to shared processor config - logger.debug("update-compute: shared proc mode") if lpar_cpu_config is None: - logger.error( - "XML parsing error: Unable to find processor configs from partition payload") - raise Exception( - "XML parsing error: Unable to find processor configs from partition payload") + raise PartitionError( + "xml parsing error: unable to find processor configs from partition payload") lpar_cpu_config.decompose() - logger.debug("get new shared processor config") new_proc_config = BeautifulSoup( get_processor_config(config), 'xml') partition_name.insert_after(new_proc_config) lpar_payload = curr_lpar except Exception as e: - logger.error( - "Exception while getting partition cpu or memory configurations") - raise e + raise PartitionError(f"failed to get partition's compute(cpu & memory) configurations, error: {e}") return lpar_payload @@ -136,16 +125,18 @@ def convert_gb_to_mb(value): def get_all_partitions(config, cookies, system_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/quick/All" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config)} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get partition list, error: {response.text}") - raise PartitionError( - f"failed to get partition list, error: {response.text}") - return response.json() + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/quick/All" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config)} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to get partition list, while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to get partition list, error: {e}") # Checks if partition exists, returns exists and if partition is created by PIM @@ -176,40 +167,43 @@ def check_partition_exists(config, cookies, system_uuid): def create_partition(config, cookies, system_uuid): - logger.debug( - f"Creating partition with name '{util.get_partition_name(config)}'") - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition" - url = "https://" + util.get_host_address(config) + uri - payload = populate_payload(config) - headers = {"x-api-key": util.get_session_key(config), - "Content-Type": CONTENT_TYPE} - response = requests.put(url, headers=headers, - data=payload, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to create partition, error: {response.text}") - raise PartitionError( - f"failed to create partition, error: {response.text}") - - soup = BeautifulSoup(response.text, 'xml') - partition_uuid = soup.find("PartitionUUID") - return partition_uuid.text + try: + logger.debug( + f"Creating partition with name '{util.get_partition_name(config)}'") + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition" + url = "https://" + util.get_host_address(config) + uri + payload = populate_payload(config) + headers = {"x-api-key": util.get_session_key(config), + "Content-Type": CONTENT_TYPE} + response = requests.put(url, headers=headers, + data=payload, cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + partition_uuid = soup.find("PartitionUUID") + return partition_uuid.text + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to create partition, while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to create partition, error: {e}") def get_partition_details(config, cookies, system_uuid, partition_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/{partition_uuid}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), - "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartition"} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error( - f"failed to get partition details, error: {response.text}") - raise PartitionError( - f"failed to get partition details, error: {response.text}") - soup = BeautifulSoup(response.text, 'xml') - lpar = str(soup.find('LogicalPartition')) - return lpar + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/{partition_uuid}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), + "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartition"} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + soup = BeautifulSoup(response.text, 'xml') + lpar = str(soup.find('LogicalPartition')) + return lpar + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to get partition details, while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise PartitionError(f"failed to get partition details, error: {e}") def edit_lpar_compute(config, cookies, system_uuid, partition_uuid): @@ -219,8 +213,6 @@ def edit_lpar_compute(config, cookies, system_uuid, partition_uuid): updated_lpar_payload = get_lpar_update_payload( config, partition_payload) if updated_lpar_payload is None: - logger.error( - f"failed to get updated lpar compute payload, error: {response.text}") raise PartitionError( f"failed to get updated lpar compute payload, error: {response.text}") @@ -232,34 +224,33 @@ def edit_lpar_compute(config, cookies, system_uuid, partition_uuid): "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartition"} response = requests.post(url, headers=headers, cookies=cookies, data=str( updated_lpar_payload), verify=False) - if response.status_code != 200: - logger.error( - f"failed to edit lpar compute, error: {response.text}") - raise PartitionError( - f"failed to edit lpar compute, error: {response.text}") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to edit lpar compute, while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise Exception(f"failed to edit lpar compute, error: {e}") logger.debug( f"Compute for partition: {partition_uuid} is updated successfully") return def set_partition_boot_string(config, cookies, system_uuid, partition_uuid, partition_payload, boot_string): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/{partition_uuid}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config), - "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartition"} - payload = BeautifulSoup(partition_payload, 'xml') - pending_boot = payload.find("PendingBootString") - pending_boot.append(boot_string) - - response = requests.post(url, headers=headers, - cookies=cookies, data=str(payload), verify=False) - if response.status_code != 200: - logger.error( - f"failed to update boot order for the partition: '{partition_uuid}', error: {response.text}") - raise PartitionError( - f"failed to update boot order for the partition: '{partition_uuid}', error: {response.text}") + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/LogicalPartition/{partition_uuid}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config), + "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=LogicalPartition"} + payload = BeautifulSoup(partition_payload, 'xml') + pending_boot = payload.find("PendingBootString") + pending_boot.append(boot_string) + + response = requests.post(url, headers=headers, + cookies=cookies, data=str(payload), verify=False) + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise PartitionError(f"failed to update boot order for the partition: '{partition_uuid}' while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise Exception(f"failed to update boot order for the partition: '{partition_uuid}', error: {e}") logger.debug( f"Updated the boot order for the partition: '{partition_uuid}'") return @@ -273,6 +264,6 @@ def remove_partition(config, cookies, partition_uuid): response = requests.delete( url, headers=headers, cookies=cookies, verify=False) if response.status_code != 204: - logger.error(f"failed to delete partition, error: {response.text}") + logger.error(f"failed to delete partition, error: '{response.text}'") return logger.debug("Partition deleted successfully") diff --git a/cli/storage/storage.py b/cli/storage/storage.py index 31c696e..0decc10 100644 --- a/cli/storage/storage.py +++ b/cli/storage/storage.py @@ -62,9 +62,7 @@ def check_if_storage_attached(vios, partition_uuid): phys_disk = physical_volume.find("VolumeName").text break except Exception as e: - logger.error( - "failed to check if storage SCSI mapping is present in VIOS") - raise e + raise StorageError(f"failed to check if storage SCSI mapping is present in VIOS, error: {e}") return found, phys_disk def check_if_vfc_disk_attached(vios, partition_uuid): @@ -86,26 +84,26 @@ def check_if_vfc_disk_attached(vios, partition_uuid): portname = port.find("PortName").text break except Exception as e: - logger.error("failed to check if storage SCSI mapping is present in VIOS") - raise e + raise StorageError(f"failed to check if storage SCSI mapping is present in VIOS, error: {e}") return found, portname def attach_storage(vios_payload, config, cookies, partition_uuid, system_uuid, vios_uuid, physical_vol_name): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/{vios_uuid}" - hmc_host = util.get_host_address(config) - url = "https://" + hmc_host + uri - payload = populate_payload( - vios_payload, hmc_host, partition_uuid, system_uuid, physical_vol_name) - headers = { - "x-api-key": util.get_session_key(config), "Content-Type": CONTENT_TYPE} - response = requests.post(url, headers=headers, - cookies=cookies, data=payload, verify=False) + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/{vios_uuid}" + hmc_host = util.get_host_address(config) + url = "https://" + hmc_host + uri + payload = populate_payload( + vios_payload, hmc_host, partition_uuid, system_uuid, physical_vol_name) + headers = { + "x-api-key": util.get_session_key(config), "Content-Type": CONTENT_TYPE} + response = requests.post(url, headers=headers, + cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error( - f"failed to attach physical disk to the partition, error: {response.text}") - raise StorageError( - f"failed to attach physical disk to the partition, error: {response.text}") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to attach physical disk to the partition, while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise StorageError(f"failed to attach physical disk to the partition, error: {e}") return @@ -127,11 +125,9 @@ def attach_physical_storage(config, cookies, sys_uuid, partition_uuid, vios_stor f"Attached '{physical_volume_name}' physical volume to the partition from VIOS '{vios_storage_uuid}'") break except (vios_operation.VIOSError, StorageError, Exception) as e: - logger.error( - f"failed to attach '{physical_volume_name}' physical storage in VIOS '{vios_storage_uuid}'") if index == len(vios_storage_list) - 1: - raise e + raise StorageError(f"failed to attach '{physical_volume_name}' physical storage in VIOS '{vios_storage_uuid}', error: {e}") else: logger.debug( - "Attempting to attach physical storage present in next available VIOS") + f"failed to attach '{physical_volume_name}' physical storage in VIOS '{vios_storage_uuid}', attempting to attach physical storage present in next available VIOS") return diff --git a/cli/storage/virtual_storage.py b/cli/storage/virtual_storage.py index e752e80..4c59816 100644 --- a/cli/storage/virtual_storage.py +++ b/cli/storage/virtual_storage.py @@ -75,11 +75,11 @@ def get_volume_group_id(config, cookies, vios_uuid, vg_name): headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} try: response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get volume group: '{response.text}'") - raise Exception("failed to get volume group") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to get volume group while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise StorageError(f"failed to get volume group, error: {e}") soup = BeautifulSoup(response.text, 'xml') entries = soup.find_all("entry") @@ -89,8 +89,7 @@ def get_volume_group_id(config, cookies, vios_uuid, vg_name): if vol_group.find("GroupName").text == vg_name: vg_id = entry.find("id").text if vg_id is None: - logger.error(f"failed to find volumegroup id corresponding to volumegroup name '{vg_name}'") - raise StorageError(f"failed to find volumegroup id corresponding to volumegroup name '{vg_name}'") + raise StorageError(f"no matching volumegroup found corresponding to volumegroup name '{vg_name}'") return vg_id def get_volume_group_details(config, cookies, vios_uuid, vg_id): @@ -100,11 +99,11 @@ def get_volume_group_details(config, cookies, vios_uuid, vg_id): headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} try: response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get volume group details: '{response.text}'") - raise Exception("failed to get volume group details") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to get volume group while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise StorageError(f"failed to get volume group details, error: {e}") soup = BeautifulSoup(response.text, 'xml') vol_group_details = str(soup.find("VolumeGroup")) return vol_group_details @@ -132,9 +131,7 @@ def check_if_vdisk_attached(vios, partition_uuid): vdisk = logical_vol.find("DiskName").text break except Exception as e: - logger.error( - "failed to check if storage SCSI mapping is present in VIOS") - raise e + raise StorageError(f"failed to check if storage SCSI mapping is present in VIOS, error: {e}") return found, vdisk # Checks if virtualdisk is created under a given volumegroup @@ -145,11 +142,7 @@ def check_if_vdisk_exists(config, cookies, vios_uuid, vg_id, vdisk): config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error( - f"failed to get volumegroup details, error: '{response.text}'") - raise Exception( - f"failed to get volumegroup details, error: '{response.text}'") + response.raise_for_status() soup = BeautifulSoup(response.text, 'xml') # remove virtual disk @@ -163,8 +156,11 @@ def check_if_vdisk_exists(config, cookies, vios_uuid, vg_id, vdisk): present = True virt_disk_xml = disk break + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to get volumegroup details while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise StorageError( + f"failed to get volumegroup details, error: '{e}'") return present, virt_disk_xml, volume_group def create_virtualdisk(config, cookies, vios_uuid, vg_id): @@ -178,11 +174,12 @@ def create_virtualdisk(config, cookies, vios_uuid, vg_id): try: payload = get_vdisk_payload(config, vg_details) response = requests.post(url, headers=headers, cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to create virtual disk: '{response.text}'") - raise StorageError(f"failed to create virtual disk") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to create virtual disk while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise StorageError(f"failed to create virtual disk, error: {e}") + logger.info("Successfully created virtual disk") def attach_virtualdisk(vios_payload, config, cookies, partition_uuid, system_uuid, vios_uuid): @@ -192,9 +189,9 @@ def attach_virtualdisk(vios_payload, config, cookies, partition_uuid, system_uui headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} try: response = requests.post(url, headers=headers, cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to attach virtual storage to the partition: '{response.text}'") - raise StorageError(f"failed to attach virtual storage to the partition") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to attach virtual storage to the partition while making http request, error: {e}, response: {e.response.text}") except Exception as e: - raise e + raise StorageError(f"failed to attach virtual storage to the partition, error: {e}") logger.info("Successfully attached virtual disk") diff --git a/cli/storage/vopt_storage.py b/cli/storage/vopt_storage.py index 37db6ec..b83b34c 100644 --- a/cli/storage/vopt_storage.py +++ b/cli/storage/vopt_storage.py @@ -59,19 +59,21 @@ def check_if_scsi_mapping_exist(partition_uuid, vios, media_dev_name): found = True break except Exception as e: - logger.error("failed to check if storage SCSI mapping is present in VIOS") - raise e + raise StorageError(f"failed to check if storage SCSI mapping is present in VIOS, error: {e}") return found def attach_vopt(vios_payload, config, cookies, partition_uuid, sys_uuid, vios_uuid, vopt_name): - uri = f"/rest/api/uom/ManagedSystem/{sys_uuid}/VirtualIOServer/{vios_uuid}" - hmc_host = util.get_host_address(config) - url = "https://" + hmc_host + uri - headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} - payload = populate_payload(config, vios_payload, hmc_host, partition_uuid, sys_uuid, vopt_name) - response = requests.post(url, headers=headers, cookies=cookies, data=payload, verify=False) + try: + uri = f"/rest/api/uom/ManagedSystem/{sys_uuid}/VirtualIOServer/{vios_uuid}" + hmc_host = util.get_host_address(config) + url = "https://" + hmc_host + uri + headers = {"x-api-key": util.get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} + payload = populate_payload(config, vios_payload, hmc_host, partition_uuid, sys_uuid, vopt_name) + response = requests.post(url, headers=headers, cookies=cookies, data=payload, verify=False) - if response.status_code != 200: - logger.error(f"failed to attach vOPT device to the partition, error: {response.text}") - raise StorageError(f"failed to attach vOPT device to the partition, error: {response.text}") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise StorageError(f"failed to attach vOPT device to the partition while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise StorageError(f"failed to attach vOPT device to the partition, error: {e}") return diff --git a/cli/utils/command_util.py b/cli/utils/command_util.py index e1562c2..dcaec2b 100644 --- a/cli/utils/command_util.py +++ b/cli/utils/command_util.py @@ -49,8 +49,7 @@ def remove_vopt_device(config, cookies, vios, vopt_name): vg_url, vol_group, media_repos = iso_util.get_media_repositories( config, cookies, vios) if media_repos is None: - logger.error("failed to get media repositories") - raise Exception("failed to get media repositories") + raise Exception(f"failed to get media repositories to remove vopt device '{vopt_name}'") found = False # remove vopt_name from media repositoy @@ -127,8 +126,7 @@ def check_if_scsi_mapping_exist(partition_uuid, vios, media_dev_name): vscsi = scsi break except Exception as e: - logger.error("failed to check if storage SCSI mapping is present in VIOS") - raise e + raise Exception(f"failed to check if storage SCSI mapping is present in VIOS, error: {e}") return found, vscsi, soup @@ -161,31 +159,31 @@ def remove_scsi_mappings(config, cookies, sys_uuid, partition_uuid, vios_uuid, v def get_system_uuid(config, cookies): - uri = "/rest/api/uom/ManagedSystem/quick/All" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key(config)} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get system UUID, error: {response.text}") - raise Exception(f"failed to get system UUID, error: {response.text}") - systems = [] try: - systems = response.json() - except requests.JSONDecodeError as e: - logger.error( - f"failed to parse json while getting UUID of the system, error: {e}") - raise - - uuid = "" - sys_name = util.get_system_name(config) - for system in systems: - if system["SystemName"] == sys_name: - uuid = system["UUID"] - break + uri = "/rest/api/uom/ManagedSystem/quick/All" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key(config)} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + systems = [] + try: + systems = response.json() + except requests.JSONDecodeError as e: + raise Exception(f"failed to parse json while getting UUID of the system, error: {e}") + + uuid = "" + sys_name = util.get_system_name(config) + for system in systems: + if system["SystemName"] == sys_name: + uuid = system["UUID"] + break - if "" == uuid: - logger.error(f"no system available with name '{sys_name}'") - raise Exception(f"no system available with name '{sys_name}'") + if "" == uuid: + raise Exception(f"no system available with name '{sys_name}'") + return uuid - return uuid + except requests.exceptions.RequestException as e: + raise Exception(f"failed to get system UUID, while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise Exception(f"failed to get system UUID, error: {e}") diff --git a/cli/utils/common.py b/cli/utils/common.py index 0948969..77da67a 100644 --- a/cli/utils/common.py +++ b/cli/utils/common.py @@ -53,8 +53,7 @@ def create_dir(path): if not os.path.isdir(path): os.mkdir(path) except OSError as e: - logger.error(f"failed to create '{path}' directory, error: {e}") - raise + raise Exception(f"failed to create '{path}' directory, error: {e}") def file_checksum(file): @@ -142,8 +141,6 @@ def ssh_to_partition(config): break except Exception as e: if i == 49: - logger.error( - f"failed to establish SSH connection to partition after 50 retries, error: {e}") raise paramiko.SSHException( f"failed to establish SSH connection to partition after 50 retries, error: {e}") logger.debug("Not able to SSH to the partition yet, retrying..") @@ -156,8 +153,7 @@ def create_dir(path): if not os.path.isdir(path): os.mkdir(path) except OSError as e: - logger.error(f"failed to create '{path}' directory, error: {e}") - raise + raise Exception(f"failed to create '{path}' directory, error: {e}") def initialize_config(config_file_path): @@ -169,8 +165,7 @@ def initialize_config(config_file_path): flavor_name = util.get_partition_flavor(config) config["partition-flavor"] = load_partition_flavor(flavor_name) except Exception as e: - logger.error(f"failed to parse {config_file_path}, error: {e}") - raise e + raise Exception(f"failed to parse {config_file_path}, error: {e}") return config @@ -198,10 +193,8 @@ def generate_ssh_keys(config): result = subprocess.run( cmd, capture_output=True, text=True, shell=True) if result.returncode != 0: - logger.error( - f"failed to run ssh-keygen command to generate keypair, error: {result.stderr} \n {result.stdout}") raise Exception( - f"failed to run ssh-keygen command to generate keypair, error: {result.stderr}") + f"failed to run ssh-keygen command to generate keypair, error: {result.stderr}\n {result.stdout}") logger.debug("SSH keypair generated successfully") # Compares dir1 & dir2 and returns True if there is a difference(either new files introduced in dir2 or changes in file contents between the two dirs) diff --git a/cli/utils/iso_util.py b/cli/utils/iso_util.py index 4e66a3c..2696e2d 100644 --- a/cli/utils/iso_util.py +++ b/cli/utils/iso_util.py @@ -61,10 +61,8 @@ def generate_cloud_init_iso_file(iso_dir, config, config_dir): try: subprocess.run(generate_cmd.split(), check=True, capture_output=True) - except subprocess.CalledProcessError as e: - logger.error( - f"failed to generate cloud-init ISO via mkisofs, error: {e.stderr}") - raise + except subprocess.CalledProcessError as e: + raise Exception(f"failed to generate cloud-init ISO via mkisofs, error: {e.stderr}\n {e.stdout}") def download_bootstrap_iso(iso_dir, config): @@ -109,9 +107,10 @@ def download_bootstrap_iso(iso_dir, config): logger.debug( "Integrity of downloaded bootstrap iso has been successfully verified..") except requests.exceptions.RequestException as e: - logger.error( - f"failed to download '{get_bootstrap_iso(config)}' file, error: {e}") - raise + raise Exception(f"failed to download '{get_bootstrap_iso(config)}' file while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise Exception(f"failed to download '{get_bootstrap_iso(config)}' file, error: {e}") + logger.debug("Download completed for bootstrap ISO file") return @@ -128,9 +127,9 @@ def download_bootstrap_checksum(checksum_url, checksum_file_path): for chunk in response.iter_content(chunk_size=8192): csum_file.write(chunk) except requests.exceptions.RequestException as e: - logger.error( - f"failed to download '{checksum_file_path}' file, error: {e}") - raise + raise Exception(f"failed to download '{checksum_file_path}' file while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise Exception(f"failed to download '{checksum_file_path}' file, error: {e}") return @@ -152,13 +151,11 @@ def remove_iso_file(config, cookies, filename, file_uuid): response = requests.delete( url, headers=headers, cookies=cookies, verify=False) if response.status_code != 204: - logger.error( - f"failed to remove ISO file '{filename}' from VIOS after uploading to media repository, error: {response.text}") raise Exception( f"failed to remove ISO file '{filename}' from VIOS after uploading to media repository, error: {response.text}") except Exception as e: logger.error( - f"Failed to remove ISO file '{filename}' from VIOS after uploading to media repository, error {e}") + f"failed to remove ISO file '{filename}' from VIOS after uploading to media repository, error {e}") logger.debug(f"ISO file: '{filename}' removed from VIOS successfully") return @@ -226,14 +223,13 @@ def upload_iso_to_media_repository(config, cookies, iso_dir, iso_file_name, sys_ logger.debug(f"Uploaded '{iso_file_name}' to vios '{vios_uuid}'") return vios_uuid except Exception as e: - logger.error(f"failed to upload ISO to '{vios_uuid}' VIOS") if file_uuid != "": remove_iso_file(config, cookies, iso_file, file_uuid) if index == len(vios_uuid_list)-1: - raise e + raise Exception(f"failed to upload ISO to '{vios_uuid}' VIOS, error: {e}") else: logger.debug( - "Upload of ISO file will be attempted on next available VIOS") + "failed to upload ISO to '{vios_uuid}' VIOS, ISO upload will be attempted on next available VIOS") return @@ -257,17 +253,14 @@ def create_iso_path(config, cookies, vios_uuid, filename, checksum, filesize): try: response = requests.put(url, headers=headers, data=payload, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error( - f"failed to create ISO path for file '{filename}', error: {response.text}") - raise Exception( - f"failed to create ISO path for file '{filename}', error: {response.text}") + response.raise_for_status() # extract file uuid from response soup = BeautifulSoup(response.text, "xml") file_uuid = soup.find("FileUUID").text + except requests.exceptions.RequestException as e: + raise Exception(f"failed to create ISO path for file '{filename}' while making http request, error: {e}, response: {e.response.text}") except Exception as e: - logger.error(f"failed to create ISO path, error: {e}") - raise e + raise Exception(f"failed to create ISO path for file '{filename}', error: {e}") logger.debug(f"{filename} ISO path created successfully") return file_uuid @@ -289,15 +282,11 @@ def readfile(f, chunksize): try: response = requests.put(url, headers=headers, data=readfile( filehandle, chunksize=65536), cookies=cookies, verify=False) - if response.status_code != 204: - logger.error( - f"failed to upload ISO file '{filehandle}' to VIOS media repository, error: {response.text}") - raise Exception( - f"failed to upload ISO file '{filehandle}' to VIOS media repository, error: {response.text}") + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise Exception(f"failed to upload ISO file '{filehandle}' to VIOS media repository while making http request, error: {e}, response: {e.response.text}") except Exception as e: - logger.error( - f"failed to upload ISO file '{filehandle}' to VIOS media repository, error: {e}") - raise e + raise Exception(f"failed to upload ISO file '{filehandle}' to VIOS media repository, error: {e}") return @@ -332,7 +321,6 @@ def get_media_repositories(config, cookies, vios): if storage_pool.find("link") is not None: vg_url = storage_pool.find("link").attrs['href'] else: - logger.error("failed to get volume group hyperlink from VIOS") raise Exception("failed to get volume group hyperlink from VIOS") # make REST call to volume group URL(vg_url) to get list of media repositories @@ -340,16 +328,13 @@ def get_media_repositories(config, cookies, vios): config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} response = requests.get(vg_url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error( - f"failed to get media repositories, error: {response.text}") - raise Exception( - f"failed to get media repositories, error: {response.text}") + response.raise_for_status() soup = BeautifulSoup(response.text, 'xml') media_repos = soup.find("MediaRepositories") vol_group = soup.find("VolumeGroup") + except requests.exceptions.RequestException as e: + raise Exception(f"failed to get media repositories, while making http request, error: {e}, response: {e.response.text}") except Exception as e: - logger.error(f"failed to get media repositories, error: {e}") - raise e + raise Exception(f"failed to get media repositories, error: {e}") logger.debug("Obtained media repositories from VIOS successfully") return vg_url, vol_group, media_repos diff --git a/cli/utils/monitor_util.py b/cli/utils/monitor_util.py index 7ebc37d..3fe4abb 100644 --- a/cli/utils/monitor_util.py +++ b/cli/utils/monitor_util.py @@ -36,16 +36,12 @@ def monitor_pim_boot(config): return if "base_config.service: Failed with result 'exit-code'" in out: ssh_client.close() - logger.error(f"failed to start AI application. error: {out}") raise Exception(f"failed to start AI application. error: {out}") else: ssh_client.close() - logger.error( - "failed to find '/etc/systemd/system/base_config.service', please check console for more possible errors") raise Exception( "failed to find '/etc/systemd/system/base_config.service', please check console for more possible errors") except Exception as e: - logger.error(f"failed to monitor PIM boot, error: {e}") raise Exception(f"failed to monitor PIM boot, error: {e}") @@ -73,8 +69,6 @@ def monitor_pim(config): else: logger.info(f"AI application is up and running, Response: {msg}") return - logger.error( - f"failed to bring up AI application from PIM image, error: {msg}") raise AIAppError( f"failed to bring up AI application from PIM image, error: {msg}") @@ -106,17 +100,15 @@ def monitor_bootstrap_boot(config): break else: logger.error( - "Failed to detect bootc based PIM AI image install completion signature. Please check console logs for more information\n") + "failed to detect bootc based PIM AI image install completion signature. Please check console logs for more information\n") if "getcontainer.service: Failed with result 'exit-code'" in out: ssh_client.close() - logger.error(f"failed to detect bootc based PIM AI image install completion signature. error: {out}") raise Exception(f"failed to detect bootc based PIM AI image install completion signature. error: {out}") else: logger.debug( "Could not find 'getcontainer.service', will look for 'base_config.service' in PIM boot since it could be a re-run and bootstrap might have already finished") ssh_client.close() except Exception as e: - logger.error(f"failed to monitor bootstrap boot, error: {e}") raise Exception(f"failed to monitor bootstrap boot, error: {e}") return diff --git a/cli/utils/validator.py b/cli/utils/validator.py index ef5b9d5..72e63c3 100644 --- a/cli/utils/validator.py +++ b/cli/utils/validator.py @@ -250,24 +250,26 @@ def validate_virtual_network_name(config, cookies, system_uuid): # Validate if the provided virtual switch name exists in the system def validate_virtual_switch_name(config, cookies, system_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualSwitch/quick/All" - url = "https://" + get_host_address(config) + uri - headers = {"x-api-key": get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml"} - response = requests.get(url, headers=headers, cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to list virtual switch. {response.text}") - raise Exception("failed to list virtual switch") - - found = False - for switch in response.json(): - # Remove '(Default)' from switch name before checking equality - if switch["SwitchName"].rstrip("(Default)") == get_vswitch_name(config): - found = True - break - - if not found: - logger.error(f"validation failed: 'partition.network.connection.virtual-switch-name' value '{get_vswitch_name(config)}' is not present in system") - + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualSwitch/quick/All" + url = "https://" + get_host_address(config) + uri + headers = {"x-api-key": get_session_key(config), "Content-Type": "application/vnd.ibm.powervm.uom+xml"} + response = requests.get(url, headers=headers, cookies=cookies, verify=False) + response.raise_for_status() + + found = False + for switch in response.json(): + # Remove '(Default)' from switch name before checking equality + if switch["SwitchName"].rstrip("(Default)") == get_vswitch_name(config): + found = True + break + + if not found: + logger.error(f"validation failed: 'partition.network.connection.virtual-switch-name' value '{get_vswitch_name(config)}' is not present in system") + except requests.exceptions.RequestException as e: + raise Exception(f"failed to list virtual switch while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise Exception(f"failed to list virtual switch, error: {e}") return found def validate_json(value, field): diff --git a/cli/vios/vios.py b/cli/vios/vios.py index 21484c0..d33ef98 100644 --- a/cli/vios/vios.py +++ b/cli/vios/vios.py @@ -14,42 +14,46 @@ logger = common.get_logger("vios") def get_vios_details(config, cookies, system_uuid, vios_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/{vios_uuid}" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key( - config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get VIOS details for '{vios_uuid}', error") - raise VIOSError(f"failed to get VIOS details for '{vios_uuid}', error") - - soup = BeautifulSoup(response.text, 'xml') - vios = str(soup.find("VirtualIOServer")) + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/{vios_uuid}" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key( + config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + vios = str(soup.find("VirtualIOServer")) + except requests.exceptions.RequestException as e: + raise VIOSError(f"failed to get VIOS details for '{vios_uuid}' while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise VIOSError(f"failed to get VIOS details for '{vios_uuid}', error: {e}") return vios def get_vios_uuid_list(config, cookies, system_uuid): - uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/quick/All" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key( - config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get VIOS list, error: {response.text}") - raise VIOSError(f"failed to get VIOS list, error: {response.text}") - - uuids = [] - sys_name = util.get_system_name(config) - for vios in response.json(): - if vios["SystemName"] == sys_name: - uuids.append(vios["UUID"]) - - if len(uuids) == 0: - logger.error(f"no VIOS available for '{sys_name}'") - raise VIOSError(f"no VIOS available for '{sys_name}'") - + try: + uri = f"/rest/api/uom/ManagedSystem/{system_uuid}/VirtualIOServer/quick/All" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key( + config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; Type=VirtualIOServer"} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + + uuids = [] + sys_name = util.get_system_name(config) + for vios in response.json(): + if vios["SystemName"] == sys_name: + uuids.append(vios["UUID"]) + + if len(uuids) == 0: + raise VIOSError(f"no VIOS available for '{sys_name}'") + except requests.exceptions.RequestException as e: + raise VIOSError(f"failed to get VIOS list while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise VIOSError(f"failed to get VIOS list, error: {e}") return uuids @@ -128,20 +132,23 @@ def get_vios_with_physical_storage(config, active_vios_servers): def get_volume_group(config, cookies, vios_uuid, vg_name): - uri = f"/rest/api/uom/VirtualIOServer/{vios_uuid}/VolumeGroup" - url = "https://" + util.get_host_address(config) + uri - headers = {"x-api-key": util.get_session_key( - config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} - response = requests.get(url, headers=headers, - cookies=cookies, verify=False) - if response.status_code != 200: - logger.error(f"failed to get volume group, error: {response.text}") - raise Exception(f"failed to get volume group, error: {response.text}") - - soup = BeautifulSoup(response.text, 'xml') - group = soup.find("GroupName", string=vg_name) - vol_group = group.parent - vg_id = vol_group.find("AtomID").text + try: + uri = f"/rest/api/uom/VirtualIOServer/{vios_uuid}/VolumeGroup" + url = "https://" + util.get_host_address(config) + uri + headers = {"x-api-key": util.get_session_key( + config), "Content-Type": "application/vnd.ibm.powervm.uom+xml; type=VolumeGroup"} + response = requests.get(url, headers=headers, + cookies=cookies, verify=False) + response.raise_for_status() + + soup = BeautifulSoup(response.text, 'xml') + group = soup.find("GroupName", string=vg_name) + vol_group = group.parent + vg_id = vol_group.find("AtomID").text + except requests.exceptions.RequestException as e: + raise VIOSError(f"failed to get volume group while making http request, error: {e}, response: {e.response.text}") + except Exception as e: + raise VIOSError(f"failed to get volume group, error: {e}") return vg_id def cleanup_logical_volume(config, cookies, vios, vios_uuid, sys_uuid, partition_uuid): @@ -212,6 +219,8 @@ def cleanup_vios(config, cookies, sys_uuid, partition_uuid, vios_uuid_list): _ = cleanup_storage(config, cookies, vios, vios_uuid, sys_uuid, partition_uuid) except Exception as e: logger.error(f"failed to clean up VIOS, error: {e}") + return False + return True def find_vios_with_vopt_mounted(config, cookies, sys_uuid, partition_uuid, vios_uuid_list, vopt_name): diff --git a/docs/deployer-guide.md b/docs/deployer-guide.md index 31ed3a4..0b43db2 100644 --- a/docs/deployer-guide.md +++ b/docs/deployer-guide.md @@ -20,7 +20,7 @@ On IBMi run source <(curl -sL https://raw.githubusercontent.com/IBM/project-pim/main/install_ibmi.sh) ``` -On Linux run +On Linux(RHEL/Fedora/CentOS) run ```shell source <(curl -sL https://raw.githubusercontent.com/IBM/project-pim/main/install_linux.sh) ``` diff --git a/examples/hmc-agent/app/hmc.py b/examples/hmc-agent/app/hmc.py index ef2efeb..9b4d408 100644 --- a/examples/hmc-agent/app/hmc.py +++ b/examples/hmc-agent/app/hmc.py @@ -105,7 +105,7 @@ def get_logical_partitions(system_name): if system["SystemName"] == system_name: system_uuid = system["UUID"] if system_uuid == "": - return f"failed to find system with name '{system_name}'" + return f"no system available with name '{system_name}'" uri = f"/rest/api/uom/LogicalPartition/quick/All" url = f"https://{os.getenv("HMC_IP")}/{uri}" diff --git a/install_linux.sh b/install_linux.sh index 24c3371..10a0d6b 100755 --- a/install_linux.sh +++ b/install_linux.sh @@ -1,6 +1,6 @@ #!/bin/bash -dnf install -y python3-pip libxml2-devel libxslt-devel rust cargo python-devel libffi-devel mkisofs +dnf install -y python3-pip libxml2-devel libxslt-devel rust cargo python-devel libffi-devel xorriso dnf groupinstall -y "Development Tools" pip install uv