diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index edd1d78ad..77b120333 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -73,6 +73,27 @@ def _udev_settle(): return +def _check_for_iscsi(): + """Connect iSCSI shared connected via iBFT or OF. + + iscsistart -f will print the iBFT or OF info. + In case such connection exists, we would like to issue + iscsistart -b to create a session to the target. + - if no connection is detected we simply return. + """ + try: + utils.execute('iscsistart', '-f') + except (processutils.ProcessExecutionError, EnvironmentError) as e: + LOG.debug("No iscsi connection detected. Skipping iscsi. " + "Error: %s", e) + return + try: + utils.execute('iscsistart', '-b') + except processutils.ProcessExecutionError as e: + LOG.warning("Something went wrong executing 'iscsistart -b' " + "Error: %s", e) + + def list_all_block_devices(block_type='disk'): """List all physical block devices @@ -86,7 +107,6 @@ def list_all_block_devices(block_type='disk'): :param block_type: Type of block device to find :return: A list of BlockDevices """ - _udev_settle() columns = ['KNAME', 'MODEL', 'SIZE', 'ROTA', 'TYPE'] @@ -425,6 +445,7 @@ class GenericHardwareManager(HardwareManager): def evaluate_hardware_support(self): # Do some initialization before we declare ourself ready + _check_for_iscsi() self._wait_for_disks() return HardwareSupport.GENERIC diff --git a/ironic_python_agent/tests/unit/test_agent.py b/ironic_python_agent/tests/unit/test_agent.py index 96ca0a95b..58d27b0b0 100644 --- a/ironic_python_agent/tests/unit/test_agent.py +++ b/ironic_python_agent/tests/unit/test_agent.py @@ -271,9 +271,10 @@ class TestBaseAgent(test_base.BaseTestCase): @mock.patch.object(time, 'sleep', autospec=True) @mock.patch('wsgiref.simple_server.make_server', autospec=True) + @mock.patch.object(hardware, '_check_for_iscsi', autospec=True) @mock.patch.object(hardware.HardwareManager, 'list_hardware_info') - def test_run_with_sleep(self, mocked_list_hardware, wsgi_server_cls, - mocked_sleep): + def test_run_with_sleep(self, mock_check_for_iscsi, mocked_list_hardware, + wsgi_server_cls, mocked_sleep): CONF.set_override('inspection_callback_url', '', enforce_type=True) wsgi_server = wsgi_server_cls.return_value wsgi_server.start.side_effect = KeyboardInterrupt() @@ -299,6 +300,7 @@ class TestBaseAgent(test_base.BaseTestCase): self.agent.heartbeater.start.assert_called_once_with() mocked_sleep.assert_called_once_with(10) + self.assertTrue(mock_check_for_iscsi.called) def test_async_command_success(self): result = base.AsyncCommandResult('foo_command', {'fail': False}, @@ -437,10 +439,11 @@ class TestAdvertiseAddress(test_base.BaseTestCase): self.assertFalse(mock_exec.called) self.assertFalse(mock_gethostbyname.called) + @mock.patch.object(hardware, '_check_for_iscsi', autospec=True) @mock.patch.object(hardware.GenericHardwareManager, 'get_ipv4_addr', autospec=True) - def test_with_network_interface(self, mock_get_ipv4, mock_exec, - mock_gethostbyname): + def test_with_network_interface(self, mock_get_ipv4, mock_check_for_iscsi, + mock_exec, mock_gethostbyname): self.agent.network_interface = 'em1' mock_get_ipv4.return_value = '1.2.3.4' @@ -450,10 +453,14 @@ class TestAdvertiseAddress(test_base.BaseTestCase): mock_get_ipv4.assert_called_once_with(mock.ANY, 'em1') self.assertFalse(mock_exec.called) self.assertFalse(mock_gethostbyname.called) + self.assertTrue(mock_check_for_iscsi.called) + @mock.patch.object(hardware, '_check_for_iscsi', autospec=True) @mock.patch.object(hardware.GenericHardwareManager, 'get_ipv4_addr', autospec=True) - def test_with_network_interface_failed(self, mock_get_ipv4, mock_exec, + def test_with_network_interface_failed(self, mock_get_ipv4, + mock_check_for_iscsi, + mock_exec, mock_gethostbyname): self.agent.network_interface = 'em1' mock_get_ipv4.return_value = None @@ -464,6 +471,7 @@ class TestAdvertiseAddress(test_base.BaseTestCase): mock_get_ipv4.assert_called_once_with(mock.ANY, 'em1') self.assertFalse(mock_exec.called) self.assertFalse(mock_gethostbyname.called) + self.assertTrue(mock_check_for_iscsi.called) def test_route_with_ip(self, mock_exec, mock_gethostbyname): self.agent.api_url = 'http://1.2.1.2:8081/v1' diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index dac4bb667..9beb48e0e 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -1241,9 +1241,11 @@ class TestGenericHardwareManager(test_base.BaseTestCase): @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices', autospec=True) + @mock.patch.object(hardware, '_check_for_iscsi', autospec=True) @mock.patch.object(time, 'sleep', autospec=True) @mock.patch.object(utils, 'guess_root_disk', autospec=True) def test_evaluate_hw_waits_for_disks(self, mocked_root_dev, mocked_sleep, + mocked_check_for_iscsi, mocked_block_dev): mocked_root_dev.side_effect = [ errors.DeviceNotFound('boom'), @@ -1252,6 +1254,7 @@ class TestGenericHardwareManager(test_base.BaseTestCase): result = self.hardware.evaluate_hardware_support() + self.assertTrue(mocked_check_for_iscsi.called) self.assertEqual(hardware.HardwareSupport.GENERIC, result) mocked_root_dev.assert_called_with(mocked_block_dev.return_value) self.assertEqual(2, mocked_root_dev.call_count) @@ -1335,9 +1338,11 @@ class TestGenericHardwareManager(test_base.BaseTestCase): @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices', autospec=True) + @mock.patch.object(hardware, '_check_for_iscsi', autospec=True) @mock.patch.object(time, 'sleep', autospec=True) @mock.patch.object(utils, 'guess_root_disk', autospec=True) def test_evaluate_hw_disks_timeout(self, mocked_root_dev, mocked_sleep, + mocked_check_for_iscsi, mocked_block_dev): mocked_root_dev.side_effect = errors.DeviceNotFound('boom') @@ -1419,6 +1424,18 @@ class TestModuleFunctions(test_base.BaseTestCase): hardware._udev_settle() mocked_execute.assert_called_once_with('udevadm', 'settle') + def test__check_for_iscsi(self, mocked_execute): + hardware._check_for_iscsi() + mocked_execute.assert_has_calls([ + mock.call('iscsistart', '-f'), + mock.call('iscsistart', '-b')]) + + def test__check_for_iscsi_no_iscsi(self, mocked_execute): + mocked_execute.side_effect = processutils.ProcessExecutionError() + hardware._check_for_iscsi() + mocked_execute.assert_has_calls([ + mock.call('iscsistart', '-f')]) + def create_hdparm_info(supported=False, enabled=False, frozen=False, enhanced_erase=False): diff --git a/releasenotes/notes/iscsi-detection-on-diskless-hw-f27dcce3aaa35ac2.yaml b/releasenotes/notes/iscsi-detection-on-diskless-hw-f27dcce3aaa35ac2.yaml new file mode 100644 index 000000000..bea18de06 --- /dev/null +++ b/releasenotes/notes/iscsi-detection-on-diskless-hw-f27dcce3aaa35ac2.yaml @@ -0,0 +1,3 @@ +--- +fixes: + - If iscsi connection present, connect an iSCSI target share before detecting disks.