diff --git a/proliantutils/ilo/client.py b/proliantutils/ilo/client.py index 09929a83..eede5165 100644 --- a/proliantutils/ilo/client.py +++ b/proliantutils/ilo/client.py @@ -267,15 +267,13 @@ class IloClient(operations.IloOperations): """ return self._call_method('set_http_boot_url', url) - def set_iscsi_boot_info(self, mac, target_name, lun, ip_address, + def set_iscsi_boot_info(self, target_name, lun, ip_address, port='3260', auth_method=None, username=None, password=None): """Set iscsi details of the system in uefi boot mode. - The iSCSI initiator is identified by the MAC provided. The initiator system is set with the target details like IQN, LUN, IP, Port etc. - :param mac: MAC address of initiator. :param target_name: Target Name for iscsi. :param lun: logical unit number. :param ip_address: IP address of the target. @@ -287,19 +285,18 @@ class IloClient(operations.IloOperations): :raises: IloCommandNotSupportedInBiosError, if the system is in the bios boot mode. """ - return self._call_method('set_iscsi_boot_info', mac, target_name, lun, + return self._call_method('set_iscsi_boot_info', target_name, lun, ip_address, port, auth_method, username, password) - def unset_iscsi_boot_info(self, mac): + def unset_iscsi_boot_info(self): """Disable iscsi boot option of the system in uefi boot mode. - :param mac: MAC address of initiator. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedInBiosError, if the system is in the bios boot mode. """ - return self._call_method('unset_iscsi_boot_info', mac) + return self._call_method('unset_iscsi_boot_info') def get_iscsi_initiator_info(self): """Returns iSCSI initiator information of iLO. diff --git a/proliantutils/ilo/operations.py b/proliantutils/ilo/operations.py index 6f9dce15..be26f8d7 100644 --- a/proliantutils/ilo/operations.py +++ b/proliantutils/ilo/operations.py @@ -64,15 +64,13 @@ class IloOperations(object): """ raise exception.IloCommandNotSupportedError(ERRMSG) - def set_iscsi_boot_info(self, mac, target_name, lun, ip_address, + def set_iscsi_boot_info(self, target_name, lun, ip_address, port='3260', auth_method=None, username=None, password=None): """Set iscsi details of the system in uefi boot mode. - The iSCSI initiator is identified by the MAC provided. The initiator system is set with the target details like IQN, LUN, IP, Port etc. - :param mac: MAC address of initiator. :param target_name: Target Name for iscsi. :param lun: logical unit number. :param ip_address: IP address of the target. @@ -86,10 +84,9 @@ class IloOperations(object): """ raise exception.IloCommandNotSupportedError(ERRMSG) - def unset_iscsi_boot_info(self, mac): + def unset_iscsi_boot_info(self): """Disable iscsi boot option of the system in uefi boot mode. - :param mac: MAC address of initiator. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedError, if the system is in the bios boot mode. diff --git a/proliantutils/ilo/ris.py b/proliantutils/ilo/ris.py index 8d0c52d8..459dd9fb 100755 --- a/proliantutils/ilo/ris.py +++ b/proliantutils/ilo/ris.py @@ -608,52 +608,38 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations): ' does not exist') raise exception.IloCommandNotSupportedError(msg) - def _change_iscsi_settings(self, mac, iscsi_info): - """Change iscsi settings. + def _change_iscsi_settings(self, iscsi_info): + """Change iSCSI settings. - :param mac: MAC address of the initiator. - :param iscsi_info: A dictionary that contains information of iscsi + :param iscsi_info: A dictionary that contains information of iSCSI target like target_name, lun, ip_address, port etc. - :raises: IloInvalidInputError, if mac provided is invalid. :raises: IloError, on an error from iLO. """ headers, bios_uri, bios_settings = self._check_bios_resource() - # Get the Boot resource and Mappings resource. + # Get the Mappings resource. map_settings = self._get_bios_mappings_resource(bios_settings) - boot_settings = self._get_bios_boot_resource(bios_settings) - correlatable_id = None - for boot_setting in boot_settings['BootSources']: - if(mac in boot_setting['UEFIDevicePath']): - correlatable_id = boot_setting['CorrelatableID'] - break + nics = [] + for mapping in map_settings['BiosPciSettingsMappings']: + for subinstance in mapping['Subinstances']: + for association in subinstance['Associations']: + if 'NicBoot' in association: + nics.append(association) - if not correlatable_id: - msg = ('MAC provided is Invalid') - raise exception.IloInvalidInputError(msg) - - nic = None - # Get the NIC for the particular mac provided. - for map_setting in map_settings['BiosPciSettingsMappings']: - sub_instances = map_setting['Subinstances'] - if sub_instances: - for sub_instance in sub_instances: - if(sub_instance['CorrelatableID'] == - correlatable_id): - # The nic is in the format 'NicBoot1' or 'NicBoot2' - nic = sub_instance['Associations'][0] - break - if nic is not None: - break - - if not nic: - msg = ('MAC does not have any corresponding mapping') + if not nics: + msg = ('No nics found') raise exception.IloError(msg) iscsi_uri = self._check_iscsi_rest_patch_allowed() - iscsi_info['iSCSIBootAttemptName'] = nic - iscsi_info['iSCSINicSource'] = nic - iscsi_info['iSCSIBootAttemptInstance'] = 1 - patch_data = {'iSCSIBootSources': [iscsi_info]} + # Set iSCSI info to all nics + iscsi_infos = [] + for nic in nics: + data = iscsi_info.copy() + data['iSCSIBootAttemptName'] = nic + data['iSCSINicSource'] = nic + data['iSCSIBootAttemptInstance'] = nics.index(nic) + 1 + iscsi_infos.append(data) + + patch_data = {'iSCSIBootSources': iscsi_infos} status, headers, response = self._rest_patch(iscsi_uri, None, patch_data) if status >= 300: @@ -905,15 +891,13 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations): msg = 'set_http_boot_url is not supported in the BIOS boot mode' raise exception.IloCommandNotSupportedInBiosError(msg) - def set_iscsi_boot_info(self, mac, target_name, lun, ip_address, + def set_iscsi_boot_info(self, target_name, lun, ip_address, port='3260', auth_method=None, username=None, password=None): - """Set iscsi details of the system in uefi boot mode. + """Set iSCSI details of the system in UEFI boot mode. - The iSCSI initiator is identified by the MAC provided. The initiator system is set with the target details like IQN, LUN, IP, Port etc. - :param mac: MAC address of initiator. :param target_name: Target Name for iscsi. :param lun: logical unit number. :param ip_address: IP address of the target. @@ -923,7 +907,7 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations): :param password: CHAP secret. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedInBiosError, if the system is - in the bios boot mode. + in the BIOS boot mode. """ if(self._is_boot_mode_uefi() is True): iscsi_info = {} @@ -937,24 +921,23 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations): iscsi_info['iSCSIAuthenticationMethod'] = 'Chap' iscsi_info['iSCSIChapUsername'] = username iscsi_info['iSCSIChapSecret'] = password - self._change_iscsi_settings(mac.upper(), iscsi_info) + self._change_iscsi_settings(iscsi_info) else: - msg = 'iscsi boot is not supported in the BIOS boot mode' + msg = 'iSCSI boot is not supported in the BIOS boot mode' raise exception.IloCommandNotSupportedInBiosError(msg) - def unset_iscsi_boot_info(self, mac): - """Disable iscsi boot option in uefi boot mode. + def unset_iscsi_boot_info(self): + """Disable iSCSI boot option in UEFI boot mode. - :param mac: MAC address of initiator. :raises: IloError, on an error from iLO. :raises: IloCommandNotSupportedInBiosError, if the system is - in the bios boot mode. + in the BIOS boot mode. """ if(self._is_boot_mode_uefi() is True): iscsi_info = {'iSCSIBootEnable': 'Disabled'} - self._change_iscsi_settings(mac.upper(), iscsi_info) + self._change_iscsi_settings(iscsi_info) else: - msg = 'iscsi boot is not supported in the BIOS boot mode' + msg = 'iSCSI boot is not supported in the BIOS boot mode' raise exception.IloCommandNotSupportedInBiosError(msg) def get_iscsi_initiator_info(self): diff --git a/proliantutils/tests/ilo/ris_sample_outputs.py b/proliantutils/tests/ilo/ris_sample_outputs.py index 6afa0fd9..52d80566 100755 --- a/proliantutils/tests/ilo/ris_sample_outputs.py +++ b/proliantutils/tests/ilo/ris_sample_outputs.py @@ -1603,6 +1603,33 @@ GET_BIOS_BOOT = """ """ +GET_BIOS_MAPPINGS_WITHOUT_NIC = """ +{ + "Registry": "HpBiosAttributeRegistryP89.1.1.00", + "BiosPciSettingsMappings": [ + { + "Associations": [ + "EmbSata1Enable" + ], + "CorrelatableID": "PciRoot(0x0)/Pci(0x1F,0x2)", + "Instance": 1, + "Subinstances": [] + }, + { + "Associations": [ + "EmbNicEnable", + { + "PreBootNetwork": "EmbNic" + } + ], + "CorrelatableID": "PciRoot(0x0)/Pci(0x1C,0x4)/Pci(0x0,0x0)", + "Instance": 3, + "Subinstances": [] + } + ] +} +""" + GET_BIOS_MAPPINGS = """ { "Registry": "HpBiosAttributeRegistryP89.1.1.00", @@ -1955,6 +1982,33 @@ GET_ISCSI_PATCH = """ "iSCSITargetIpAddress": "10.10.1.30", "iSCSITargetName": "iqn.2011-07.com.example.server:test1", "iSCSITargetTcpPort": 3260 + }, + { + "iSCSIBootAttemptInstance": 2, + "iSCSIBootAttemptName": "NicBoot2", + "iSCSIBootLUN": "1", + "iSCSINicSource": "NicBoot2", + "iSCSITargetIpAddress": "10.10.1.30", + "iSCSITargetName": "iqn.2011-07.com.example.server:test1", + "iSCSITargetTcpPort": 3260 + }, + { + "iSCSIBootAttemptInstance": 3, + "iSCSIBootAttemptName": "NicBoot3", + "iSCSIBootLUN": "1", + "iSCSINicSource": "NicBoot3", + "iSCSITargetIpAddress": "10.10.1.30", + "iSCSITargetName": "iqn.2011-07.com.example.server:test1", + "iSCSITargetTcpPort": 3260 + }, + { + "iSCSIBootAttemptInstance": 4, + "iSCSIBootAttemptName": "NicBoot4", + "iSCSIBootLUN": "1", + "iSCSINicSource": "NicBoot4", + "iSCSITargetIpAddress": "10.10.1.30", + "iSCSITargetName": "iqn.2011-07.com.example.server:test1", + "iSCSITargetTcpPort": 3260 } ] } diff --git a/proliantutils/tests/ilo/test_client.py b/proliantutils/tests/ilo/test_client.py index 41052739..124b2a27 100644 --- a/proliantutils/tests/ilo/test_client.py +++ b/proliantutils/tests/ilo/test_client.py @@ -362,10 +362,10 @@ class IloClientTestCase(testtools.TestCase): @mock.patch.object(client.IloClient, '_call_method') def test_set_iscsi_boot_info(self, call_mock): - self.client.set_iscsi_boot_info('c456', 'iqn.2011-07.com:example:123', + self.client.set_iscsi_boot_info('iqn.2011-07.com:example:123', '1', '10.10.1.23', '3260', 'CHAP', 'user', 'password') - call_mock.assert_called_once_with('set_iscsi_boot_info', 'c456', + call_mock.assert_called_once_with('set_iscsi_boot_info', 'iqn.2011-07.com:example:123', '1', '10.10.1.23', '3260', 'CHAP', 'user', 'password') @@ -377,8 +377,8 @@ class IloClientTestCase(testtools.TestCase): @mock.patch.object(client.IloClient, '_call_method') def test_unset_iscsi_boot_info(self, call_mock): - self.client.unset_iscsi_boot_info('c456') - call_mock.assert_called_once_with('unset_iscsi_boot_info', 'c456') + self.client.unset_iscsi_boot_info() + call_mock.assert_called_once_with('unset_iscsi_boot_info') @mock.patch.object(client.IloClient, '_call_method') def test_set_iscsi_initiator_info(self, call_mock): diff --git a/proliantutils/tests/ilo/test_ris.py b/proliantutils/tests/ilo/test_ris.py index 007e28ed..181e0e7b 100755 --- a/proliantutils/tests/ilo/test_ris.py +++ b/proliantutils/tests/ilo/test_ris.py @@ -156,12 +156,10 @@ class IloRisTestCase(testtools.TestCase): 'iSCSITargetIpAddress': '10.10.1.30', 'iSCSITargetTcpPort': 3260} self.client.set_iscsi_boot_info( - 'C4346BB7EF30', 'iqn.2011-07.com.example.server:test1', '1', '10.10.1.30') _uefi_boot_mode_mock.assert_called_once_with() - change_iscsi_settings_mock.assert_called_once_with('C4346BB7EF30', - iscsi_variables) + change_iscsi_settings_mock.assert_called_once_with(iscsi_variables) @mock.patch.object(ris.RISOperations, '_change_iscsi_settings') @mock.patch.object(ris.RISOperations, '_is_boot_mode_uefi') @@ -169,17 +167,15 @@ class IloRisTestCase(testtools.TestCase): change_iscsi_settings_mock): _uefi_boot_mode_mock.return_value = True iscsi_variables = {'iSCSIBootEnable': 'Disabled'} - self.client.unset_iscsi_boot_info('C4346BB7EF30') + self.client.unset_iscsi_boot_info() _uefi_boot_mode_mock.assert_called_once_with() - change_iscsi_settings_mock.assert_called_once_with('C4346BB7EF30', - iscsi_variables) + change_iscsi_settings_mock.assert_called_once_with(iscsi_variables) @mock.patch.object(ris.RISOperations, '_is_boot_mode_uefi') def test_unset_iscsi_boot_info_bios(self, _uefi_boot_mode_mock): _uefi_boot_mode_mock.return_value = False - mac = 'C4346BB7EF30' self.assertRaises(exception.IloCommandNotSupportedInBiosError, - self.client.unset_iscsi_boot_info, mac) + self.client.unset_iscsi_boot_info) _uefi_boot_mode_mock.assert_called_once_with() @mock.patch.object(ris.RISOperations, '_rest_get') @@ -226,9 +222,8 @@ class IloRisTestCase(testtools.TestCase): @mock.patch.object(ris.RISOperations, '_is_boot_mode_uefi') def test_set_iscsi_boot_info_bios(self, _uefi_boot_mode_mock): _uefi_boot_mode_mock.return_value = False - mac = 'C4346BB7EF30' self.assertRaises(exception.IloCommandNotSupportedInBiosError, - self.client.set_iscsi_boot_info, mac, + self.client.set_iscsi_boot_info, 'iqn.2011-07.com.example.server:test1', '1', '10.10.1.30') _uefi_boot_mode_mock.assert_called_once_with() @@ -1333,17 +1328,14 @@ class TestRISOperationsPrivateMethods(testtools.TestCase): @mock.patch.object(ris.RISOperations, '_rest_patch') @mock.patch.object(ris.RISOperations, '_check_iscsi_rest_patch_allowed') @mock.patch.object(ris.RISOperations, '_get_bios_mappings_resource') - @mock.patch.object(ris.RISOperations, '_get_bios_boot_resource') @mock.patch.object(ris.RISOperations, '_check_bios_resource') - def test__change_iscsi_settings(self, check_bios_mock, boot_mock, + def test__change_iscsi_settings(self, check_bios_mock, mappings_mock, check_iscsi_mock, patch_mock): bios_uri = '/rest/v1/systems/1/bios' bios_settings = json.loads(ris_outputs.GET_BIOS_SETTINGS) check_bios_mock.return_value = (ris_outputs.GET_HEADERS, bios_uri, bios_settings) - boot_settings = json.loads(ris_outputs.GET_BIOS_BOOT) - boot_mock.return_value = boot_settings map_settings = json.loads(ris_outputs.GET_BIOS_MAPPINGS) mappings_mock.return_value = map_settings iscsi_uri = '/rest/v1/systems/1/bios/iScsi/Settings' @@ -1356,68 +1348,39 @@ class TestRISOperationsPrivateMethods(testtools.TestCase): check_iscsi_mock.return_value = iscsi_uri patch_mock.return_value = (200, ris_outputs.GET_HEADERS, ris_outputs.REST_POST_RESPONSE) - self.client._change_iscsi_settings('C4346BB7EF30', properties) + self.client._change_iscsi_settings(properties) check_bios_mock.assert_called_once_with() - boot_mock.assert_called_once_with(bios_settings) mappings_mock.assert_called_once_with(bios_settings) check_iscsi_mock.assert_called_once_with() patch_mock.assert_called_once_with(iscsi_uri, None, settings) @mock.patch.object(ris.RISOperations, '_get_bios_mappings_resource') - @mock.patch.object(ris.RISOperations, '_get_bios_boot_resource') @mock.patch.object(ris.RISOperations, '_check_bios_resource') - def test__change_iscsi_settings_invalid_mac(self, check_bios_mock, - boot_mock, + def test__change_iscsi_settings_without_nic(self, check_bios_mock, mappings_mock): bios_uri = '/rest/v1/systems/1/bios' bios_settings = json.loads(ris_outputs.GET_BIOS_SETTINGS) check_bios_mock.return_value = (ris_outputs.GET_HEADERS, bios_uri, bios_settings) - boot_settings = json.loads(ris_outputs.GET_BIOS_BOOT) - boot_mock.return_value = boot_settings - map_settings = json.loads(ris_outputs.GET_BIOS_MAPPINGS) - mappings_mock.return_value = map_settings - self.assertRaises(exception.IloInvalidInputError, - self.client._change_iscsi_settings, 'C456', {}) - check_bios_mock.assert_called_once_with() - boot_mock.assert_called_once_with(bios_settings) - mappings_mock.assert_called_once_with(bios_settings) - - @mock.patch.object(ris.RISOperations, '_get_bios_mappings_resource') - @mock.patch.object(ris.RISOperations, '_get_bios_boot_resource') - @mock.patch.object(ris.RISOperations, '_check_bios_resource') - def test__change_iscsi_settings_invalid_mapping(self, check_bios_mock, - boot_mock, - mappings_mock): - bios_uri = '/rest/v1/systems/1/bios' - bios_settings = json.loads(ris_outputs.GET_BIOS_SETTINGS) - check_bios_mock.return_value = (ris_outputs.GET_HEADERS, - bios_uri, bios_settings) - boot_settings = json.loads(ris_outputs.GET_BIOS_BOOT) - boot_mock.return_value = boot_settings - map_settings = json.loads(ris_outputs.GET_BIOS_MAPPINGS) + map_settings = json.loads(ris_outputs.GET_BIOS_MAPPINGS_WITHOUT_NIC) mappings_mock.return_value = map_settings self.assertRaises(exception.IloError, self.client._change_iscsi_settings, - 'C4346BB7EF31', {}) + {}) check_bios_mock.assert_called_once_with() - boot_mock.assert_called_once_with(bios_settings) mappings_mock.assert_called_once_with(bios_settings) @mock.patch.object(ris.RISOperations, '_rest_patch') @mock.patch.object(ris.RISOperations, '_check_iscsi_rest_patch_allowed') @mock.patch.object(ris.RISOperations, '_get_bios_mappings_resource') - @mock.patch.object(ris.RISOperations, '_get_bios_boot_resource') @mock.patch.object(ris.RISOperations, '_check_bios_resource') - def test__change_iscsi_settings_fail(self, check_bios_mock, boot_mock, + def test__change_iscsi_settings_fail(self, check_bios_mock, mappings_mock, check_iscsi_mock, patch_mock): bios_uri = '/rest/v1/systems/1/bios' bios_settings = json.loads(ris_outputs.GET_BIOS_SETTINGS) check_bios_mock.return_value = (ris_outputs.GET_HEADERS, bios_uri, bios_settings) - boot_settings = json.loads(ris_outputs.GET_BIOS_BOOT) - boot_mock.return_value = boot_settings map_settings = json.loads(ris_outputs.GET_BIOS_MAPPINGS) mappings_mock.return_value = map_settings iscsi_uri = '/rest/v1/systems/1/bios/iScsi/Settings' @@ -1432,9 +1395,8 @@ class TestRISOperationsPrivateMethods(testtools.TestCase): ris_outputs.REST_POST_RESPONSE) self.assertRaises(exception.IloError, self.client._change_iscsi_settings, - 'C4346BB7EF30', properties) + properties) check_bios_mock.assert_called_once_with() - boot_mock.assert_called_once_with(bios_settings) mappings_mock.assert_called_once_with(bios_settings) check_iscsi_mock.assert_called_once_with() patch_mock.assert_called_once_with(iscsi_uri, None, settings)