Merge "Use SDK for remaining ironic driver calls"

This commit is contained in:
Zuul
2023-12-13 20:00:40 +00:00
committed by Gerrit Code Review
3 changed files with 194 additions and 196 deletions
+141 -129
View File
@@ -19,7 +19,7 @@ import base64
from unittest import mock
import fixtures
from ironicclient import exc as ironic_exception
from openstack.baremetal.v1 import node as _node
from openstack import exceptions as sdk_exc
from oslo_config import cfg
from oslo_service import loopingcall
@@ -1271,13 +1271,12 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_wait_for_active')
@mock.patch.object(ironic_driver.IronicDriver,
'_add_instance_info_to_node')
def _test_spawn(self, mock_aiitn, mock_wait_active,
mock_avti, mock_node, mock_looping, mock_save,
mock_avti, mock_looping, mock_save,
config_drive_value=None):
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
@@ -1286,7 +1285,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance.flavor = fake_flavor
self.mock_conn.get_node.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation()
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation()
fake_looping_call = FakeLoopingCall()
mock_looping.return_value = fake_looping_call
@@ -1296,8 +1296,11 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.driver.spawn(self.ctx, instance, image_meta, [], None, {})
self.mock_conn.get_node.assert_called_once_with(
node_id, fields=ironic_driver._NODE_FIELDS)
mock_node.validate.assert_called_once_with(node_id)
node_id, fields=ironic_driver._NODE_FIELDS,
)
self.mock_conn.validate_node.assert_called_once_with(
node_id, required=None,
)
mock_aiitn.assert_called_once_with(node, instance,
test.MatchType(objects.ImageMeta),
fake_flavor, block_device_info=None)
@@ -1336,7 +1339,6 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, 'destroy')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_wait_for_active')
@@ -1344,7 +1346,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
'_add_instance_info_to_node')
def test_spawn_destroyed_after_failure(self, mock_aiitn,
mock_wait_active, mock_avti,
mock_destroy, mock_node,
mock_destroy,
mock_looping, mock_required_by):
mock_required_by.return_value = False
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
@@ -1353,8 +1355,9 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid)
instance.flavor = fake_flavor
mock_node.get.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation()
self.mock_conn.get_node.return_value = node
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation()
fake_looping_call = FakeLoopingCall()
mock_looping.return_value = fake_looping_call
@@ -1538,9 +1541,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
def test_spawn_node_driver_validation_fail(self, mock_avti, mock_node,
def test_spawn_node_driver_validation_fail(self, mock_avti,
mock_required_by):
mock_required_by.return_value = False
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
@@ -1549,9 +1551,12 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
instance.flavor = flavor
mock_node.validate.return_value = ironic_utils.get_test_validation(
power={'result': False}, deploy={'result': False},
storage={'result': False})
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation(
power=_node.ValidationResult(result=False, reason=None),
deploy=_node.ValidationResult(result=False, reason=None),
storage=_node.ValidationResult(result=False, reason=None),
)
self.mock_conn.get_node.return_value = node
image_meta = ironic_utils.get_test_image_meta()
@@ -1560,16 +1565,17 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.mock_conn.get_node.assert_called_once_with(
node_id, fields=ironic_driver._NODE_FIELDS)
mock_avti.assert_called_once_with(self.ctx, instance, None)
mock_node.validate.assert_called_once_with(node_id)
self.mock_conn.validate_node.assert_called_once_with(
node_id, required=None,
)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive')
def test_spawn_node_configdrive_fail(self,
mock_configdrive,
mock_avti, mock_node, mock_save,
mock_avti, mock_save,
mock_required_by):
mock_required_by.return_value = True
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
@@ -1578,7 +1584,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
instance = fake_instance.fake_instance_obj(self.ctx, node=node_id)
instance.flavor = flavor
self.mock_conn.get_node.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation()
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation()
image_meta = ironic_utils.get_test_image_meta()
mock_configdrive.side_effect = test.TestingException()
@@ -1589,16 +1596,17 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.mock_conn.get_node.assert_called_once_with(
node_id, fields=ironic_driver._NODE_FIELDS)
mock_node.validate.assert_called_once_with(node_id)
self.mock_conn.validate_node.assert_called_once_with(
node_id, required=None,
)
mock_cleanup_deploy.assert_called_with(node, instance, None)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_cleanup_deploy')
def test_spawn_node_trigger_deploy_fail(self, mock_cleanup_deploy,
mock_avti,
mock_node, mock_required_by):
mock_required_by):
mock_required_by.return_value = False
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(driver='fake', id=node_id)
@@ -1608,7 +1616,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
image_meta = ironic_utils.get_test_image_meta()
self.mock_conn.get_node.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation()
self.mock_conn.validate_node.return_value = \
ironic_utils.get_test_validation()
self.mock_conn.set_node_provision_state.side_effect = \
sdk_exc.SDKException('foo')
@@ -1620,18 +1629,19 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.mock_conn.get_node.assert_called_once_with(
node_id, fields=ironic_driver._NODE_FIELDS)
mock_node.validate.assert_called_once_with(node_id)
self.mock_conn.validate_node.assert_called_once_with(
node_id, required=None,
)
mock_cleanup_deploy.assert_called_once_with(node, instance, None)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_add_volume_target_info')
@mock.patch.object(ironic_driver.IronicDriver, '_wait_for_active')
def test_spawn_sets_default_ephemeral_device(self,
mock_wait, mock_avti,
mock_node, mock_save,
mock_save,
mock_looping,
mock_required_by):
mock_required_by.return_value = False
@@ -1645,12 +1655,11 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.assertTrue(mock_save.called)
self.assertEqual('/dev/sda1', instance.default_ephemeral_device)
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver,
'_remove_instance_info_from_node')
@mock.patch.object(ironic_driver.IronicDriver, '_cleanup_deploy')
def _test_destroy(self, state, mock_cleanup_deploy,
mock_remove_instance_info, mock_node):
mock_remove_instance_info):
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
network_info = 'foo'
@@ -1786,8 +1795,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
node.uuid, "deleted",
)
@mock.patch.object(FAKE_CLIENT, 'node')
def test_destroy_unassociate_fail(self, mock_node):
def test_destroy_unassociate_fail(self):
node_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = _get_cached_node(
driver='fake', id=node_id,
@@ -1828,25 +1836,24 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(ironic_driver.IronicDriver,
'_validate_instance_and_node')
@mock.patch.object(FAKE_CLIENT.node, 'inject_nmi')
def test_trigger_crash_dump(self, mock_nmi, fake_validate):
def test_trigger_crash_dump(self, fake_validate):
node = _get_cached_node()
fake_validate.return_value = node
instance = fake_instance.fake_instance_obj(self.ctx,
node=node.uuid)
self.driver.trigger_crash_dump(instance)
mock_nmi.assert_called_once_with(node.uuid)
self.mock_conn.inject_nmi_to_node.assert_called_once_with(node.uuid)
@mock.patch.object(ironic_driver.IronicDriver,
'_validate_instance_and_node')
@mock.patch.object(FAKE_CLIENT.node, 'inject_nmi')
def test_trigger_crash_dump_error(self, mock_nmi, fake_validate):
def test_trigger_crash_dump_error(self, fake_validate):
node = _get_cached_node()
fake_validate.return_value = node
mock_nmi.side_effect = ironic_exception.BadRequest()
self.mock_conn.inject_nmi_to_node.side_effect = \
sdk_exc.BadRequestException()
instance = fake_instance.fake_instance_obj(self.ctx,
node=node.uuid)
self.assertRaises(ironic_exception.BadRequest,
self.assertRaises(sdk_exc.BadRequestException,
self.driver.trigger_crash_dump, instance)
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@@ -2442,9 +2449,9 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.mock_conn.get_node.return_value = node
instance = fake_instance.fake_instance_obj(self.ctx)
network_info = utils.get_test_network_info()
mock_pvifs.side_effect = ironic_exception.BadRequest('fake error')
mock_pvifs.side_effect = sdk_exc.BadRequestException('fake error')
self.assertRaises(
ironic_exception.BadRequest,
sdk_exc.BadRequestException,
self.driver.prepare_networks_before_block_device_mapping,
instance, network_info)
mock_pvifs.assert_called_once_with(node, instance, network_info)
@@ -2460,7 +2467,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
def test_clean_networks_preparation_error(self, mock_upvifs):
instance = fake_instance.fake_instance_obj(self.ctx)
network_info = utils.get_test_network_info()
mock_upvifs.side_effect = ironic_exception.BadRequest('fake error')
mock_upvifs.side_effect = sdk_exc.BadRequestException('fake error')
self.driver.clean_networks_preparation(instance, network_info)
mock_upvifs.assert_called_once_with(instance, network_info)
@@ -2511,7 +2518,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
def test_prepare_for_spawn_invalid_instance(self):
instance = fake_instance.fake_instance_obj(self.ctx,
node=None)
self.assertRaises(ironic_exception.BadRequest,
self.assertRaises(exception.NovaException,
self.driver.prepare_for_spawn,
instance)
@@ -2716,33 +2723,34 @@ class IronicDriverSyncTestCase(IronicDriverTestCase):
self.driver.unrescue, self.ctx, instance,
)
def test__can_send_version(self):
@mock.patch('openstack.utils.supports_microversion')
def test__can_send_version(self, mock_supports_microversion):
mock_supports_microversion.return_value = True
version = '%d.%d' % cw.IRONIC_API_VERSION
self.assertIsNone(
self.driver._can_send_version(
min_version='%d.%d' % cw.IRONIC_API_VERSION))
self.driver._can_send_version(version)
)
def test__can_send_version_too_new(self):
self.assertRaises(exception.IronicAPIVersionNotAvailable,
self.driver._can_send_version,
min_version='%d.%d' % (cw.IRONIC_API_VERSION[0],
cw.IRONIC_API_VERSION[1] + 1))
@mock.patch('openstack.utils.supports_microversion')
def test__can_send_version_too_new(self, mock_supports_microversion):
mock_supports_microversion.return_value = False
def test__can_send_version_too_old(self):
version = '%d.%d' % (
cw.IRONIC_API_VERSION[0], cw.IRONIC_API_VERSION[1] + 1,
)
self.assertRaises(
exception.IronicAPIVersionNotAvailable,
self.driver._can_send_version,
max_version='%d.%d' % (cw.PRIOR_IRONIC_API_VERSION[0],
cw.PRIOR_IRONIC_API_VERSION[1] - 1))
version,
)
@mock.patch.object(cw.IronicClientWrapper, 'current_api_version',
autospec=True)
@mock.patch.object(cw.IronicClientWrapper, 'is_api_version_negotiated',
autospec=True)
def test__can_send_version_not_negotiated(self, mock_is_negotiated,
mock_api_version):
mock_is_negotiated.return_value = False
self.assertIsNone(self.driver._can_send_version())
self.assertFalse(mock_api_version.called)
# DELETEME
def test__can_send_version_too_old(self):
pass
def test__can_send_version_not_negotiated(self):
pass
@mock.patch.object(instance_metadata, 'InstanceMetadata')
@@ -2965,7 +2973,7 @@ class HashRingTestCase(test.NoDBTestCase):
self.flags(peer_list=['host1', 'host2'], group='ironic')
self._test__refresh_hash_ring(services, expected_hosts,
uncalled=['host3'])
mock_can_send.assert_called_once_with(min_version='1.46')
mock_can_send.assert_called_once_with('1.46')
@mock.patch.object(ironic_driver.IronicDriver, '_can_send_version')
def test__refresh_hash_ring_peer_list_old_api(self, mock_can_send):
@@ -2978,7 +2986,7 @@ class HashRingTestCase(test.NoDBTestCase):
self.flags(peer_list=['host1', 'host2'], group='ironic')
self._test__refresh_hash_ring(services, expected_hosts,
uncalled=['host3'])
mock_can_send.assert_called_once_with(min_version='1.46')
mock_can_send.assert_called_once_with('1.46')
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test__check_peer_list(self, mock_log):
@@ -3216,13 +3224,12 @@ class NodeCacheTestCase(test.NoDBTestCase):
self.assertEqual(expected_cache, self.driver.node_cache)
@mock.patch.object(FAKE_CLIENT, 'node')
class IronicDriverConsoleTestCase(test.NoDBTestCase):
@mock.patch.object(cw, 'IronicClientWrapper',
lambda *_: FAKE_CLIENT_WRAPPER)
@mock.patch.object(objects.ServiceList, 'get_all_computes_by_hv_type')
def setUp(self, mock_services):
super(IronicDriverConsoleTestCase, self).setUp()
super().setUp()
self.driver = ironic_driver.IronicDriver(fake.FakeVirtAPI())
@@ -3254,7 +3261,7 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
}
}
def test__get_node_console_with_reset_success(self, mock_node):
def test__get_node_console_with_reset_success(self):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3264,27 +3271,27 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
# Set it up so that _fake_get_console() returns 'mode'
temp_data['target_mode'] = mode
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
expected = self._create_console_data()['console_info']
result = self.driver._get_node_console_with_reset(self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertEqual(self.node.uuid, result['node'].uuid)
self.assertThat(result['console_info'],
nova_matchers.DictMatches(expected))
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test__get_node_console_with_reset_console_disabled(self, mock_log,
mock_node):
def test__get_node_console_with_reset_console_disabled(self, mock_log):
def _fake_log_debug(msg, *args, **kwargs):
regex = r'Console is disabled for instance .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.return_value = \
self.mock_conn.get_node_console.return_value = \
self._create_console_data(enabled=False)
mock_log.debug.side_effect = _fake_log_debug
@@ -3292,37 +3299,37 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
self.driver._get_node_console_with_reset,
self.instance)
mock_node.get_console.assert_called_once_with(self.node.uuid)
mock_node.set_console_mode.assert_not_called()
self.mock_conn.get_node_console.assert_called_once_with(self.node.uuid)
self.mock_conn.set_node_console_mode.assert_not_called()
self.assertTrue(mock_log.debug.called)
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test__get_node_console_with_reset_set_mode_failed(self, mock_log,
mock_node):
def test__get_node_console_with_reset_set_mode_failed(self, mock_log):
def _fake_log_error(msg, *args, **kwargs):
regex = r'Failed to set console mode .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.return_value = self._create_console_data()
mock_node.set_console_mode.side_effect = exception.NovaException()
self.mock_conn.get_node_console.return_value = \
self._create_console_data()
self.mock_conn.set_node_console_mode.side_effect = \
sdk_exc.SDKException()
mock_log.error.side_effect = _fake_log_error
self.assertRaises(exception.ConsoleNotAvailable,
self.driver._get_node_console_with_reset,
self.instance)
mock_node.get_console.assert_called_once_with(self.node.uuid)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.mock_conn.get_node_console.assert_called_once_with(self.node.uuid)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.error.called)
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test__get_node_console_with_reset_wait_failed(self, mock_log,
mock_node):
def test__get_node_console_with_reset_wait_failed(self, mock_log):
def _fake_get_console(node_uuid):
if mock_node.set_console_mode.called:
if self.mock_conn.set_node_console_mode.called:
# After the call to set_console_mode(), then _wait_state()
# will call _get_console() to check the result.
raise exception.NovaException()
raise sdk_exc.SDKException()
else:
return self._create_console_data()
@@ -3330,23 +3337,22 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
regex = r'Failed to acquire console information for instance .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.side_effect = _fake_get_console
self.mock_conn.get_node_console.side_effect = _fake_get_console
mock_log.error.side_effect = _fake_log_error
self.assertRaises(exception.ConsoleNotAvailable,
self.driver._get_node_console_with_reset,
self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.error.called)
@mock.patch.object(ironic_driver, '_CONSOLE_STATE_CHECKING_INTERVAL', 0.05)
@mock.patch.object(loopingcall, 'BackOffLoopingCall')
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test__get_node_console_with_reset_wait_timeout(self, mock_log,
mock_looping,
mock_node):
mock_looping):
CONF.set_override('serial_console_state_timeout', 1, group='ironic')
temp_data = {'target_mode': True}
@@ -3360,8 +3366,9 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
regex = r'Timeout while waiting for console mode to be set .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
mock_log.error.side_effect = _fake_log_error
mock_timer = mock_looping.return_value
@@ -3372,14 +3379,14 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
self.driver._get_node_console_with_reset,
self.instance)
self.assertEqual(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertEqual(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.error.called)
mock_timer.start.assert_called_with(starting_interval=0.05, timeout=1,
jitter=0.5)
def test_get_serial_console_socat(self, mock_node):
def test_get_serial_console_socat(self):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3388,29 +3395,30 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
def _fake_set_console_mode(node_uuid, mode):
temp_data['target_mode'] = mode
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
result = self.driver.get_serial_console(self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertIsInstance(result, console_type.ConsoleSerial)
self.assertEqual('127.0.0.1', result.host)
self.assertEqual(10000, result.port)
def test_get_serial_console_socat_disabled(self, mock_node):
mock_node.get_console.return_value = \
def test_get_serial_console_socat_disabled(self):
self.mock_conn.get_node_console.return_value = \
self._create_console_data(enabled=False)
self.assertRaises(exception.ConsoleTypeUnavailable,
self.driver.get_serial_console,
self.ctx, self.instance)
mock_node.get_console.assert_called_once_with(self.node.uuid)
mock_node.set_console_mode.assert_not_called()
self.mock_conn.get_node_console.assert_called_once_with(self.node.uuid)
self.mock_conn.set_node_console_mode.assert_not_called()
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test_get_serial_console_socat_invalid_url(self, mock_log, mock_node):
def test_get_serial_console_socat_invalid_url(self, mock_log):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3424,20 +3432,21 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
regex = r'Invalid Socat console URL .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
mock_log.error.side_effect = _fake_log_error
self.assertRaises(exception.ConsoleTypeUnavailable,
self.driver.get_serial_console,
self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.error.called)
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test_get_serial_console_socat_invalid_url_2(self, mock_log, mock_node):
def test_get_serial_console_socat_invalid_url_2(self, mock_log):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3451,21 +3460,21 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
regex = r'Invalid Socat console URL .*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
mock_log.error.side_effect = _fake_log_error
self.assertRaises(exception.ConsoleTypeUnavailable,
self.driver.get_serial_console,
self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.error.called)
@mock.patch.object(ironic_driver, 'LOG', autospec=True)
def test_get_serial_console_socat_unsupported_scheme(self, mock_log,
mock_node):
def test_get_serial_console_socat_unsupported_scheme(self, mock_log):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3479,19 +3488,20 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
regex = r'Socat serial console only supports \"tcp\".*'
self.assertThat(msg, matchers.MatchesRegex(regex))
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
mock_log.warning.side_effect = _fake_log_warning
self.assertRaises(exception.ConsoleTypeUnavailable,
self.driver.get_serial_console,
self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertTrue(mock_log.warning.called)
def test_get_serial_console_socat_tcp6(self, mock_node):
def test_get_serial_console_socat_tcp6(self):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3501,18 +3511,19 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
def _fake_set_console_mode(node_uuid, mode):
temp_data['target_mode'] = mode
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
result = self.driver.get_serial_console(self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
self.assertIsInstance(result, console_type.ConsoleSerial)
self.assertEqual('::1', result.host)
self.assertEqual(10000, result.port)
def test_get_serial_console_shellinabox(self, mock_node):
def test_get_serial_console_shellinabox(self):
temp_data = {'target_mode': True}
def _fake_get_console(node_uuid):
@@ -3522,12 +3533,13 @@ class IronicDriverConsoleTestCase(test.NoDBTestCase):
def _fake_set_console_mode(node_uuid, mode):
temp_data['target_mode'] = mode
mock_node.get_console.side_effect = _fake_get_console
mock_node.set_console_mode.side_effect = _fake_set_console_mode
self.mock_conn.get_node_console.side_effect = _fake_get_console
self.mock_conn.set_node_console_mode.side_effect = \
_fake_set_console_mode
self.assertRaises(exception.ConsoleTypeUnavailable,
self.driver.get_serial_console,
self.ctx, self.instance)
self.assertGreater(mock_node.get_console.call_count, 1)
self.assertEqual(2, mock_node.set_console_mode.call_count)
self.assertGreater(self.mock_conn.get_node_console.call_count, 1)
self.assertEqual(2, self.mock_conn.set_node_console_mode.call_count)
+11 -6
View File
@@ -13,18 +13,23 @@
# License for the specific language governing permissions and limitations
# under the License.
from openstack.baremetal.v1 import node as _node
from nova import objects
from nova.virt.ironic import client_wrapper
from nova.virt.ironic import ironic_states
def get_test_validation(**kw):
return type('interfaces', (object,),
{'power': kw.get('power', {'result': True}),
'deploy': kw.get('deploy', {'result': True}),
'console': kw.get('console', True),
'rescue': kw.get('rescue', True),
'storage': kw.get('storage', {'result': True})})()
result = {
'power': _node.ValidationResult(result=True, reason=None),
'deploy': _node.ValidationResult(result=True, reason=None),
'console': _node.ValidationResult(result=True, reason=None),
'rescue': _node.ValidationResult(result=True, reason=None),
'storage': _node.ValidationResult(result=True, reason=None),
}
result.update(kw)
return result
def get_test_node(fields=None, **kw):
+42 -61
View File
@@ -26,13 +26,12 @@ import tempfile
import time
from urllib import parse as urlparse
import microversion_parse
from openstack import exceptions as sdk_exc
from openstack import utils as sdk_utils
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_service import loopingcall
from oslo_utils import excutils
from oslo_utils import importutils
from tooz import hashring as hash_ring
from nova.api.metadata import base as instance_metadata
@@ -59,8 +58,6 @@ from nova.virt.ironic import patcher
from nova.virt import netutils
ironic = None
LOG = logging.getLogger(__name__)
@@ -185,16 +182,12 @@ class IronicDriver(virt_driver.ComputeDriver):
rebalances_nodes = True
def __init__(self, virtapi, read_only=False):
super(IronicDriver, self).__init__(virtapi)
global ironic
if ironic is None:
ironic = importutils.import_module('ironicclient')
super().__init__(virtapi)
self.node_cache = {}
self.node_cache_time = 0
self.servicegroup_api = servicegroup.API()
self.ironicclient = client_wrapper.IronicClientWrapper()
self._ironic_connection = None
@property
@@ -388,9 +381,11 @@ class IronicDriver(virt_driver.ComputeDriver):
LOG.debug('Preparing to spawn instance %s.', instance.uuid)
node_uuid = instance.get('node')
if not node_uuid:
raise ironic.exc.BadRequest(
_("Ironic node uuid not supplied to "
"driver for instance %s.") % instance.uuid)
msg = _(
"Ironic node uuid not supplied to "
"driver for instance %s."
) % instance.uuid
raise exception.NovaException(msg)
node = self._get_node(node_uuid)
# Its possible this node has just moved from deleting
@@ -711,7 +706,7 @@ class IronicDriver(virt_driver.ComputeDriver):
# filter by conductor_group. If it cannot, limiting to
# peer_list could end up with a node being managed by multiple
# compute services.
self._can_send_version(min_version='1.46')
self._can_send_version('1.46')
peer_list = set(CONF.ironic.peer_list)
# these configs are mutable; need to check at runtime and init.
@@ -771,7 +766,7 @@ class IronicDriver(virt_driver.ComputeDriver):
conductor_group = CONF.ironic.conductor_group
if conductor_group is not None:
try:
self._can_send_version(min_version='1.46')
self._can_send_version('1.46')
nodes = _get_node_list(conductor_group=conductor_group)
LOG.debug('Limiting manageable ironic nodes to conductor '
'group %s', conductor_group)
@@ -1153,9 +1148,10 @@ class IronicDriver(virt_driver.ComputeDriver):
# is a significant issue. It may mean we've been passed the wrong data.
node_uuid = instance.get('node')
if not node_uuid:
raise ironic.exc.BadRequest(
raise exception.NovaException(
_("Ironic node uuid not supplied to "
"driver for instance %s.") % instance.uuid)
"driver for instance %s.") % instance.uuid
)
node = self._get_node(node_uuid)
flavor = instance.flavor
@@ -1181,20 +1177,27 @@ class IronicDriver(virt_driver.ComputeDriver):
instance.save()
# validate we are ready to do the deploy
validate_chk = self.ironicclient.call("node.validate", node_uuid)
if (not validate_chk.deploy.get('result') or
not validate_chk.power.get('result') or
not validate_chk.storage.get('result')):
# NOTE(stephenfin): we don't pass required since we have to do our own
# validation
validate_chk = self.ironic_connection.validate_node(
node_uuid,
required=None,
)
if (
not validate_chk['deploy'].result or
not validate_chk['power'].result or
not validate_chk['storage'].result
):
# something is wrong. undo what we have done
self._cleanup_deploy(node, instance, network_info)
raise exception.ValidationError(_(
"Ironic node: %(id)s failed to validate."
" (deploy: %(deploy)s, power: %(power)s,"
" storage: %(storage)s)")
"Ironic node: %(id)s failed to validate. "
"(deploy: %(deploy)s, power: %(power)s, "
"storage: %(storage)s)")
% {'id': node.uuid,
'deploy': validate_chk.deploy,
'power': validate_chk.power,
'storage': validate_chk.storage})
'deploy': validate_chk['deploy'],
'power': validate_chk['power'],
'storage': validate_chk['storage']})
# Config drive
configdrive_value = None
@@ -1526,7 +1529,7 @@ class IronicDriver(virt_driver.ComputeDriver):
LOG.debug('Trigger crash dump called for instance', instance=instance)
node = self._validate_instance_and_node(instance)
self.ironicclient.call("node.inject_nmi", node.uuid)
self.ironic_connection.inject_nmi_to_node(node.uuid)
LOG.info('Successfully triggered crash dump into Ironic node %s',
node.uuid, instance=instance)
@@ -1806,12 +1809,10 @@ class IronicDriver(virt_driver.ComputeDriver):
node_uuid = node.uuid
def _get_console():
"""Request ironicclient to acquire node console."""
"""Request to acquire node console."""
try:
return self.ironicclient.call('node.get_console', node_uuid)
except (exception.NovaException, # Retry failed
ironic.exc.InternalServerError, # Validations
ironic.exc.BadRequest) as e: # Maintenance
return self.ironic_connection.get_node_console(node_uuid)
except sdk_exc.SDKException as e:
LOG.error('Failed to acquire console information for '
'instance %(inst)s: %(reason)s',
{'inst': instance.uuid, 'reason': e})
@@ -1829,13 +1830,10 @@ class IronicDriver(virt_driver.ComputeDriver):
return False
def _enable_console(mode):
"""Request ironicclient to enable/disable node console."""
"""Request to enable/disable node console."""
try:
self.ironicclient.call('node.set_console_mode', node_uuid,
mode)
except (exception.NovaException, # Retry failed
ironic.exc.InternalServerError, # Validations
ironic.exc.BadRequest) as e: # Maintenance
self.ironic_connection.set_node_console_mode(node_uuid, mode)
except sdk_exc.SDKException as e:
LOG.error('Failed to set console mode to "%(mode)s" '
'for instance %(inst)s: %(reason)s',
{'mode': mode,
@@ -2111,30 +2109,13 @@ class IronicDriver(virt_driver.ComputeDriver):
return None
def _can_send_version(self, min_version=None, max_version=None):
def _can_send_version(self, version=None):
"""Validate if the supplied version is available in the API."""
# NOTE(TheJulia): This will effectively just be a pass if no
# version negotiation has occured, since there is no way for
# us to know without explicitly otherwise requesting that
# back-end negotiation occurs. This is a capability that is
# present in python-ironicclient, however it may not be needed
# in this case.
if self.ironicclient.is_api_version_negotiated:
current_api_version = self.ironicclient.current_api_version
if (min_version and
microversion_parse.parse_version_string(
current_api_version) <
microversion_parse.parse_version_string(
min_version)):
raise exception.IronicAPIVersionNotAvailable(
version=min_version)
if (max_version and
microversion_parse.parse_version_string(
current_api_version) >
microversion_parse.parse_version_string(
max_version)):
raise exception.IronicAPIVersionNotAvailable(
version=max_version)
if not sdk_utils.supports_microversion(
self.ironic_connection,
version,
):
raise exception.IronicAPIVersionNotAvailable(version=version)
def rescue(self, context, instance, network_info, image_meta,
rescue_password, block_device_info):