def reset_ilo(self): """Resets the iLO. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedError, if the command is not supported on the server. """ reset_uri = '/rest/v1/Managers/1' status, headers, manager = self._rest_get(reset_uri) if status != 200: msg = self._get_extended_error(manager) raise exception.IloError(msg) # verify expected type mtype = self._get_type(manager) if (mtype not in ['Manager.0', 'Manager.1']): msg = "%s is not a valid Manager type " % mtype raise exception.IloError(msg) action = {'Action': 'Reset'} # perform the POST status, headers, response = self._rest_post(reset_uri, None, action) if (status != 200): msg = self._get_extended_error(response) raise exception.IloError(msg)
def get_persistent_boot_device(self): """Get current persistent boot device set for the host :returns: persistent boot device for the system :raises: IloError, on an error from iLO. """ boot_string = None if not self.persistent_boot_config_order or not self.boot_sources: msg = ('Boot sources or persistent boot config order not found') LOG.debug(msg) raise exception.IloError(msg) preferred_boot_device = self.persistent_boot_config_order[0] for boot_source in self.boot_sources: if ((boot_source.get("StructuredBootString") is not None) and (preferred_boot_device == boot_source.get("StructuredBootString"))): boot_string = boot_source["BootString"] break else: msg = (('Persistent boot device failed, as no matched boot ' 'sources found for device: %(persistent_boot_device)s') % { 'persistent_boot_device': preferred_boot_device }) LOG.debug(msg) raise exception.IloError(msg) for key, value in BOOT_SOURCE_TARGET_TO_PARTIAL_STRING_MAP.items(): for val in value: if val in boot_string: return key return sushy.BOOT_SOURCE_TARGET_NONE
def get_essential_properties(self): """Get the essential scheduling properties :returns: a dictionary containing memory size, disk size, number of cpus, cpu arch, port numbers and mac addresses. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedError, if the command is not supported on the server. """ data = self._call_method('get_essential_properties') if (data['properties']['local_gb'] == 0): cred = self.snmp_credentials if cred and cred.get('snmp_inspection'): disksize = snmp.get_local_gb(self.host, cred) if disksize: data['properties']['local_gb'] = disksize else: msg = self._('Snmp inspection failed to ' 'get the disk size') LOG.debug(msg) raise exception.IloError(msg) else: msg = self._("Inspection failed to get the disk size") LOG.debug(msg) raise exception.IloError(msg) return data
def _parse_processor_embedded_health(self, data): """Parse the get_host_health_data() for essential properties :param data: the output returned by get_host_health_data() :returns: processor details like cpu arch and number of cpus. """ processor = self.get_value_as_list( (data['GET_EMBEDDED_HEALTH_DATA']['PROCESSORS']), 'PROCESSOR') if processor is None: msg = "Unable to get cpu data. Error: Data missing" raise exception.IloError(msg) cpus = 0 for proc in processor: for val in proc.values(): processor_detail = val['VALUE'] proc_core_threads = processor_detail.split('; ') for x in proc_core_threads: if "thread" in x: v = x.split() try: cpus = cpus + int(v[0]) except ValueError: msg = ("Unable to get cpu data. " "The Value %s returned couldn't be " "manipulated to get number of " "actual processors" % processor_detail) raise exception.IloError(msg) cpu_arch = 'x86_64' return cpus, cpu_arch
def _change_bios_setting(self, properties): """Change the bios settings to specified values.""" # Get the keys to check if keys are supported. keys = properties.keys() # Check if the BIOS resource/property if exists. headers, bios_uri, bios_settings = self._check_bios_resource(keys) # if this BIOS resource doesn't support PATCH, go get the Settings. if not self._operation_allowed(headers, 'PATCH'): # this is GET-only bios_uri = bios_settings['links']['Settings']['href'] status, headers, bios_settings = self._rest_get(bios_uri) # this should allow PATCH, else raise error if not self._operation_allowed(headers, 'PATCH'): msg = ('PATCH Operation not supported on the resource' '%s ' % bios_uri) raise exception.IloError(msg) request_headers = dict() if self.bios_password: bios_password_hash = hashlib.sha256( (self.bios_password.encode()).hexdigest().upper()) request_headers['X-HPRESTFULAPI-AuthToken'] = bios_password_hash # perform the patch status, headers, response = self._rest_patch(bios_uri, request_headers, properties) if status >= 300: msg = self._get_extended_error(response) raise exception.IloError(msg)
def _parse_nics_embedded_health(self, data): """Gets the NIC details from get_embedded_health data Parse the get_host_health_data() for essential properties :param data: the output returned by get_host_health_data() :returns: a dictionary of port numbers and their corresponding mac addresses. :raises IloError, if unable to get NIC data. """ nic_data = self.get_value_as_list( (data['GET_EMBEDDED_HEALTH_DATA'][self.NIC_INFORMATION_TAG]), 'NIC') if nic_data is None: msg = "Unable to get NIC details. Data missing" raise exception.IloError(msg) nic_dict = {} for item in nic_data: try: port = item['NETWORK_PORT']['VALUE'] mac = item['MAC_ADDRESS']['VALUE'] self._update_nic_data_from_nic_info_based_on_model( nic_dict, item, port, mac) except KeyError: msg = "Unable to get NIC details. Data missing" raise exception.IloError(msg) return nic_dict
def _get_collection(self, collection_uri, request_headers=None): """Generator function that returns collection members.""" # get the collection status, headers, thecollection = self._rest_get(collection_uri) if status != 200: msg = self._get_extended_error(thecollection) raise exception.IloError(msg) while status < 300: # verify expected type # Don't limit to version 0 here as we will rev to 1.0 at some # point hopefully with minimal changes ctype = self._get_type(thecollection) if (ctype not in ['Collection.0', 'Collection.1']): raise exception.IloError("collection not found") # if this collection has inline items, return those # NOTE: Collections are very flexible in how the represent # members. They can be inline in the collection as members # of the 'Items' array, or they may be href links in the # links/Members array. The could actually be both. Typically, # iLO implements the inline (Items) for only when the collection # is read only. We have to render it with the href links when an # array contains PATCHable items because its complex to PATCH # inline collection members. if 'Items' in thecollection: # iterate items for item in thecollection['Items']: # if the item has a self uri pointer, # supply that for convenience. memberuri = None if 'links' in item and 'self' in item['links']: memberuri = item['links']['self']['href'] yield 200, None, item, memberuri # else walk the member links elif ('links' in thecollection and 'Member' in thecollection['links']): # iterate members for memberuri in thecollection['links']['Member']: # for each member return the resource indicated by the # member link status, headers, member = self._rest_get(memberuri['href']) yield status, headers, member, memberuri['href'] # page forward if there are more pages in the collection if ('links' in thecollection and 'NextPage' in thecollection['links']): next_link_uri = ( collection_uri + '?page=' + str(thecollection['links']['NextPage']['page'])) status, headers, thecollection = self._rest_get(next_link_uri) # else we are finished iterating the collection else: break
def _get_host_details(self): """Get the system details.""" # Assuming only one system present as part of collection, # as we are dealing with iLO's here. status, headers, system = self._rest_get('/rest/v1/Systems/1') if status < 300: stype = self._get_type(system) if stype not in ['ComputerSystem.0', 'ComputerSystem.1']: msg = "%s is not a valid system type " % stype raise exception.IloError(msg) else: msg = self._get_extended_error(system) raise exception.IloError(msg) return system
def reset_bios_to_default(self): """Resets the BIOS settings to default values. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedError, if the command is not supported on the server. """ # Check if the BIOS resource if exists. headers_bios, bios_uri, bios_settings = self._check_bios_resource() # Get the default configs base_config_uri = bios_settings['links']['BaseConfigs']['href'] status, headers, config = self._rest_get(base_config_uri) if status != 200: msg = self._get_extended_error(config) raise exception.IloError(msg) # if this BIOS resource doesn't support PATCH, go get the Settings if not self._operation_allowed(headers_bios, 'PATCH'): # this is GET-only bios_uri = bios_settings['links']['Settings']['href'] status, headers, bios_settings = self._rest_get(bios_uri) # this should allow PATCH, else raise error if not self._operation_allowed(headers, 'PATCH'): msg = ('PATCH Operation not supported on the resource' '%s ' % bios_uri) raise exception.IloError(msg) new_bios_settings = {} for cfg in config['BaseConfigs']: default_settings = cfg.get('default', None) if default_settings is not None: new_bios_settings = default_settings break request_headers = dict() if self.bios_password: bios_password_hash = hashlib.sha256( (self.bios_password.encode()).hexdigest().upper()) request_headers['X-HPRESTFULAPI-AuthToken'] = bios_password_hash # perform the patch status, headers, response = self._rest_patch(bios_uri, request_headers, new_bios_settings) if status >= 300: msg = self._get_extended_error(response) raise exception.IloError(msg)
def _parse_memory_embedded_health(self, data): """Parse the get_host_health_data() for essential properties :param data: the output returned by get_host_health_data() :returns: memory size in MB. :raises IloError, if unable to get the memory details. """ memory_mb = 0 memory = self.get_value_as_list((data['GET_EMBEDDED_HEALTH_DATA'] ['MEMORY']), 'MEMORY_DETAILS_SUMMARY') if memory is None: msg = "Unable to get memory data. Error: Data missing" raise exception.IloError(msg) total_memory_size = 0 for item in memory: for val in item.values(): memsize = val['TOTAL_MEMORY_SIZE']['VALUE'] if memsize != 'N/A': memory_bytes = ( strutils.string_to_bytes( memsize.replace(' ', ''), return_int=True)) memory_mb = int(memory_bytes / (1024 * 1024)) total_memory_size = total_memory_size + memory_mb return total_memory_size
def _get_response_body_from_gzipped_content(self, url, response): """Get the response body from gzipped content Try to decode as gzip (we should check the headers for Content-Encoding=gzip) if response.headers['content-encoding'] == "gzip": ... :param url: the url for which response was sent :type url: str :param response: response content object, probably gzipped :type response: object :returns: returns response body :raises IloError: if the content is **not** gzipped """ try: gzipper = gzip.GzipFile(fileobj=six.BytesIO(response.text)) LOG.debug(self._("Received compressed response for " "url %(url)s."), {'url': url}) uncompressed_string = (gzipper.read().decode('UTF-8')) response_body = json.loads(uncompressed_string) except Exception as e: LOG.debug( self._("Error occurred while decompressing body. " "Got invalid response '%(response)s' for " "url %(url)s: %(error)s"), {'url': url, 'response': response.text, 'error': e}) raise exception.IloError(e) return response_body
def delete_raid(self): """Delete the raid configuration on the hardware. Loops through each SmartStorageConfig controller and clears the raid configuration. :raises: IloError, on an error from iLO. """ self.check_smart_storage_config_ids() any_exceptions = [] ld_exc_count = 0 for config_id in self.smart_storage_config_identities: try: ssc_obj = self.get_smart_storage_config(config_id) ssc_obj.delete_raid() except exception.IloLogicalDriveNotFoundError as e: ld_exc_count += 1 except sushy.exceptions.SushyError as e: any_exceptions.append((config_id, str(e))) if any_exceptions: msg = ('The Redfish controller failed to delete the ' 'raid configuration in one or more controllers with ' 'Error: %(error)s' % { 'error': str(any_exceptions) }) raise exception.IloError(msg) if ld_exc_count == len(self.smart_storage_config_identities): msg = ('No logical drives are found in any controllers. Nothing ' 'to delete.') raise exception.IloLogicalDriveNotFoundError(msg)
def _check_bios_resource(self, properties=[]): """Check if the bios resource exists.""" system = self._get_host_details() if ('links' in system['Oem']['Hp'] and 'BIOS' in system['Oem']['Hp']['links']): # Get the BIOS URI and Settings bios_uri = system['Oem']['Hp']['links']['BIOS']['href'] status, headers, bios_settings = self._rest_get(bios_uri) if status >= 300: msg = self._get_extended_error(bios_settings) raise exception.IloError(msg) # If property is not None, check if the bios_property is supported for property in properties: if property not in bios_settings: # not supported on this platform msg = ('\tBIOS Property "' + property + '" is not' ' supported on this system.') raise exception.IloCommandNotSupportedError(msg) return headers, bios_uri, bios_settings else: msg = ('"links/BIOS" section in ComputerSystem/Oem/Hp' ' does not exist') raise exception.IloCommandNotSupportedError(msg)
def get_secure_boot_mode(self): """Get the status of secure boot. :returns: True, if enabled, else False :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedError, if the command is not supported on the server. """ system = self._get_host_details() if ('links' not in system['Oem']['Hp'] or 'SecureBoot' not in system['Oem']['Hp']['links']): msg = ('"SecureBoot" resource or feature is not supported' ' on this system') raise exception.IloCommandNotSupportedError(msg) secure_boot_uri = system['Oem']['Hp']['links']['SecureBoot']['href'] # get the Secure Boot object status, headers, secure_boot_settings = self._rest_get(secure_boot_uri) if status >= 300: msg = self._get_extended_error(system) raise exception.IloError(msg) return secure_boot_settings['SecureBootCurrentState']
def _change_secure_boot_settings(self, property, value): """Change secure boot settings on the server.""" system = self._get_host_details() # find the BIOS URI if ('links' not in system['Oem']['Hp'] or 'SecureBoot' not in system['Oem']['Hp']['links']): msg = (' "SecureBoot" resource or feature is not ' 'supported on this system') raise exception.IloCommandNotSupportedError(msg) secure_boot_uri = system['Oem']['Hp']['links']['SecureBoot']['href'] # Change the property required new_secure_boot_settings = dict() new_secure_boot_settings[property] = value # perform the patch status, headers, response = self._rest_patch(secure_boot_uri, None, new_secure_boot_settings) if status >= 300: msg = self._get_extended_error(response) raise exception.IloError(msg) # Change the bios setting as a workaround to enable secure boot # Can be removed when fixed for Gen9 snap2 val = self._get_bios_setting('CustomPostMessage') val = val.rstrip() if val.endswith(" ") else val + " " self._change_bios_setting({'CustomPostMessage': val})
def test_wait_for_ilo_after_reset_fail(self, name_mock, time_mock): exc = exception.IloError('error') name_mock.side_effect = exc self.assertRaises(exception.IloConnectionError, common.wait_for_ilo_after_reset, self.ribcl) self.assertEqual(common.RETRY_COUNT, name_mock.call_count) name_mock.assert_called_with()
def test_wait_for_ribcl_firmware_update_to_complete_retries_till_exception( self, get_product_name_mock, sleep_mock): # | GIVEN | exc = exception.IloError('error') get_product_name_mock.side_effect = ['Rap metal', 'Death metal', exc] # | WHEN | common.wait_for_ribcl_firmware_update_to_complete(self.ribcl) # | THEN | self.assertEqual(3, get_product_name_mock.call_count)
def test_wait_for_ilo_after_reset_retry(self, name_mock, sleep_mock): # | GIVEN | exc = exception.IloError('error') name_mock.side_effect = [exc, ribcl_output.GET_PRODUCT_NAME] # | WHEN | common.wait_for_ilo_after_reset(self.ribcl) # | THEN | self.assertEqual(2, name_mock.call_count) name_mock.assert_called_with()
def test__call_method_redfish_4(self, redfish_mock, ribcl_product_name_mock): ribcl_product_name_mock.side_effect = ( exception.IloError('RIBCL is disabled')) redfish_mock.return_value.get_product_name.return_value = 'Gen10' self.client = client.IloClient("1.2.3.4", "admin", "secret") self.assertRaises(NotImplementedError, self.client._call_method, 'reset_ilo')
def test_wait_for_ilo_after_reset_fail(self, name_mock, time_mock): # | GIVEN | exc = exception.IloError('error') name_mock.side_effect = exc # | WHEN | & | THEN | self.assertRaises(exception.IloError, common.wait_for_ilo_after_reset, self.ribcl) self.assertEqual(10, name_mock.call_count) name_mock.assert_called_with()
def check_smart_storage_config_ids(self): """Check SmartStorageConfig controllers is there in hardware. :raises: IloError, on an error from iLO. """ if self.smart_storage_config_identities is None: msg = ('The Redfish controller failed to get the ' 'SmartStorageConfig controller configurations.') LOG.debug(msg) raise exception.IloError(msg)
def test_wait_for_ris_firmware_update_to_complete_fail( self, get_firmware_update_progress_mock, sleep_mock): # | GIVEN | exc = exception.IloError('error') get_firmware_update_progress_mock.side_effect = exc # | WHEN | & | THEN | self.assertRaises(exception.IloError, common.wait_for_ris_firmware_update_to_complete, self.ris) self.assertEqual(10, get_firmware_update_progress_mock.call_count)
def _validate_message(self, message): """Validate XML response from iLO. This function validates the XML response to see if the exit status is 0 or not in the response. If the status is non-zero it raises exception. """ if message.tag != 'RIBCL': # the true case shall be unreachable for response # XML from Ilo as all messages are tagged with RIBCL # but still raise an exception if any invalid # XML response is returned by Ilo. Set status to some # arbitary non-zero value. status = -1 raise exception.IloClientInternalError(message, status) for child in message: if child.tag != 'RESPONSE': return message status = int(child.get('STATUS'), 16) msg = child.get('MESSAGE') if status == 0 and msg != 'No error': return msg if status != 0: if 'syntax error' in msg or 'Feature not supported' in msg: for cmd in BOOT_MODE_CMDS: if cmd in msg: platform = self.get_product_name() msg = ("%(cmd)s is not supported on %(platform)s" % { 'cmd': cmd, 'platform': platform }) LOG.debug( self._("Got invalid response with " "message: '%(message)s'"), {'message': msg}) raise (exception.IloCommandNotSupportedError( msg, status)) else: LOG.debug( self._("Got invalid response with " "message: '%(message)s'"), {'message': msg}) raise exception.IloClientInternalError(msg, status) if (status in exception.IloLoginFailError.statuses or msg in exception.IloLoginFailError.messages): LOG.debug( self._("Got invalid response with " "message: '%(message)s'"), {'message': msg}) raise exception.IloLoginFailError(msg, status) LOG.debug( self._("Got invalid response with " "message: '%(message)s'"), {'message': msg}) raise exception.IloError(msg, status)
def flash_firmware(self, redfish_inst, file_url): """Perform firmware flashing on a redfish system :param file_url: url to firmware bits. :param redfish_inst: redfish instance :raises: IloError, on an error from iLO. """ action_data = { 'ImageURI': file_url, } target_uri = self._get_firmware_update_element().target_uri try: self._conn.post(target_uri, data=action_data) except sushy.exceptions.SushyError as e: msg = (('The Redfish controller failed to update firmware ' 'with file %(file)s Error %(error)s') % { 'file': file_url, 'error': str(e) }) LOG.debug(msg) # noqa raise exception.IloError(msg) self.wait_for_redfish_firmware_update_to_complete(redfish_inst) try: state, percent = self.get_firmware_update_progress() except sushy.exceptions.SushyError as e: msg = ('Failed to get firmware progress update ' 'Error %(error)s' % { 'error': str(e) }) LOG.debug(msg) raise exception.IloError(msg) if state == "Error": msg = 'Unable to update firmware' LOG.debug(msg) # noqa raise exception.IloError(msg) elif state == "Unknown": msg = 'Status of firmware update not known' LOG.debug(msg) # noqa else: # "Complete" | "Idle" LOG.info('Flashing firmware file: %s ... done', file_url)
def update_persistent_boot(self, devices=[], persistent=False): """Changes the persistent boot device order in BIOS boot mode for host Note: It uses first boot device from the devices and ignores rest. :param devices: ordered list of boot devices :param persistent: Boolean flag to indicate if the device to be set as a persistent boot device :raises: IloError, on an error from iLO. :raises: IloInvalidInputError, if the given input is not valid. """ device = PERSISTENT_BOOT_DEVICE_MAP.get(devices[0].upper()) if device == sushy.BOOT_SOURCE_TARGET_UEFI_TARGET: try: uefi_devices = self.uefi_target_override_devices iscsi_device = None for uefi_device in uefi_devices: if uefi_device is not None and 'iSCSI' in uefi_device: iscsi_device = uefi_device break if iscsi_device is None: msg = 'No UEFI iSCSI bootable device found on system.' raise exception.IloError(msg) except sushy.exceptions.SushyError as e: msg = ('Unable to get uefi target override devices. ' 'Error %s') % (str(e)) raise exception.IloError(msg) uefi_boot_settings = { 'Boot': { 'UefiTargetBootSourceOverride': iscsi_device } } self._conn.patch(self.path, data=uefi_boot_settings) elif device is None: device = sushy.BOOT_SOURCE_TARGET_NONE tenure = (sushy.BOOT_SOURCE_ENABLED_CONTINUOUS if persistent else sushy.BOOT_SOURCE_ENABLED_ONCE) self.set_system_boot_source(device, enabled=tenure)
def test__call_method_redfish_3(self, redfish_mock, ribcl_product_name_mock): ribcl_product_name_mock.side_effect = ( exception.IloError('RIBCL is disabled')) redfish_mock.return_value.get_product_name.return_value = 'Gen10' self.client = client.IloClient("1.2.3.4", "admin", "secret") redfish_get_host_power_mock = ( redfish.RedfishOperations.return_value.get_host_power_status) self.client._call_method('get_host_power_status') redfish_get_host_power_mock.assert_called_once_with()
def test_wait_for_ris_firmware_update_to_complete_retry_on_exception( self, get_firmware_update_progress_mock, sleep_mock): # | GIVEN | exc = exception.IloError('error') get_firmware_update_progress_mock.side_effect = [('PROGRESSING', 25), exc, ('COMPLETED', 100)] # | WHEN | common.wait_for_ris_firmware_update_to_complete(self.ris) # | THEN | self.assertEqual(3, get_firmware_update_progress_mock.call_count)
def _get_disk_boot_devices(self, result): disk_list = [] try: for item in result: if common.isDisk(item["DESCRIPTION"]): disk_list.append(item["value"]) except KeyError as e: msg = "_get_disk_boot_devices failed with the KeyError:%s" raise exception.IloError((msg) % e) return disk_list
def test_update_firmware_vmedia_attach_fails(self, client_mock, validate_mock): ilo_mock_object = client_mock.return_value eject_media_mock = ilo_mock_object.eject_virtual_media value = ("Unable to attach SUM SPP iso http://1.2.3.4/SPP.iso " "to the iLO") eject_media_mock.side_effect = exception.IloError(value) exc = self.assertRaises(exception.IloError, sum_controller.update_firmware, self.node) self.assertEqual(value, str(exc))
def _get_virtual_boot_devices(self, result): virtual_list = [] dev_desc = "HP iLO Virtual USB CD" try: for item in result: if dev_desc in item["DESCRIPTION"]: virtual_list.append(item["value"]) except KeyError as e: msg = "_get_virtual_boot_devices failed with the KeyError:%s" raise exception.IloError((msg) % e) return virtual_list