Use SDK for remaining ironic driver calls

We would like nova not to use ironicclient, but instead to invoke the
ironic API directly through an openstacksdk connection.

This concludes our migration of the driver and switches the remaining
ironic calls to use SDK.

Change-Id: Ibb5b168ee0944463b996e96f033bd3dfb498e304
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane 2022-12-02 13:17:45 +00:00 committed by Stephen Finucane
parent 71edcc3758
commit 68170e0411
3 changed files with 194 additions and 196 deletions

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')
@ -2440,9 +2447,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)
@ -2458,7 +2465,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)
@ -2509,7 +2516,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)
@ -2714,33 +2721,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')
@ -2963,7 +2971,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):
@ -2976,7 +2984,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):
@ -3214,13 +3222,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())
@ -3252,7 +3259,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):
@ -3262,27 +3269,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
@ -3290,37 +3297,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()
@ -3328,23 +3335,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}
@ -3358,8 +3364,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
@ -3370,14 +3377,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):
@ -3386,29 +3393,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):
@ -3422,20 +3430,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):
@ -3449,21 +3458,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):
@ -3477,19 +3486,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):
@ -3499,18 +3509,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):
@ -3520,12 +3531,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)

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):

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):