Delay rendering configdrive
When the configdrive input is JSON (meta_data, etc), delay the rendering until the ISO image is actually used. It has two benefits: 1) Avoid storing a large ISO image in instance_info, 2) Allow deploy steps to access the original user's input. Fix configdrive masking to correctly mask dicts. Story: #2008875 Task: #42419 Change-Id: I86d30bbb505b8c794bfa6412606f4516f8885aa9
This commit is contained in:
parent
a362bbc9e4
commit
172d1b22df
@ -1423,6 +1423,9 @@ def node_sanitize(node, fields):
|
||||
if not show_instance_secrets and node.get('instance_info'):
|
||||
node['instance_info'] = strutils.mask_dict_password(
|
||||
node['instance_info'], "******")
|
||||
# NOTE(dtantsur): configdrive may be a dict
|
||||
if node['instance_info'].get('configdrive'):
|
||||
node['instance_info']['configdrive'] = "******"
|
||||
# NOTE(tenbrae): agent driver may store a swift temp_url on the
|
||||
# instance_info, which shouldn't be exposed to non-admin users.
|
||||
# Now that ironic supports additional policies, we need to hide
|
||||
|
@ -130,8 +130,6 @@ def do_node_deploy(task, conductor_id=None, configdrive=None,
|
||||
utils.wipe_deploy_internal_info(task)
|
||||
try:
|
||||
if configdrive:
|
||||
if isinstance(configdrive, dict):
|
||||
configdrive = utils.build_configdrive(node, configdrive)
|
||||
_store_configdrive(node, configdrive)
|
||||
except (exception.SwiftOperationError, exception.ConfigInvalid) as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
@ -417,6 +415,10 @@ def _store_configdrive(node, configdrive):
|
||||
|
||||
"""
|
||||
if CONF.deploy.configdrive_use_object_store:
|
||||
# Don't store the JSON source in swift.
|
||||
if isinstance(configdrive, dict):
|
||||
configdrive = utils.build_configdrive(node, configdrive)
|
||||
|
||||
# NOTE(lucasagomes): No reason to use a different timeout than
|
||||
# the one used for deploying the node
|
||||
timeout = (CONF.conductor.configdrive_swift_temp_url_duration
|
||||
|
@ -1005,6 +1005,21 @@ def build_configdrive(node, configdrive):
|
||||
vendor_data=configdrive.get('vendor_data'))
|
||||
|
||||
|
||||
def get_configdrive_image(node):
|
||||
"""Get configdrive as an ISO image or a URL.
|
||||
|
||||
Converts the JSON representation into an image. URLs and raw contents
|
||||
are returned unchanged.
|
||||
|
||||
:param node: an Ironic node object.
|
||||
:returns: A gzipped and base64 encoded configdrive as a string.
|
||||
"""
|
||||
configdrive = node.instance_info.get('configdrive')
|
||||
if isinstance(configdrive, dict):
|
||||
configdrive = build_configdrive(node, configdrive)
|
||||
return configdrive
|
||||
|
||||
|
||||
def fast_track_able(task):
|
||||
"""Checks if the operation can be a streamlined deployment sequence.
|
||||
|
||||
|
@ -561,7 +561,11 @@ class AgentDeploy(CustomAgentDeploy):
|
||||
if disk_label is not None:
|
||||
image_info['disk_label'] = disk_label
|
||||
|
||||
configdrive = node.instance_info.get('configdrive')
|
||||
configdrive = manager_utils.get_configdrive_image(node)
|
||||
if configdrive:
|
||||
# FIXME(dtantsur): remove this duplication once IPA is ready:
|
||||
# https://review.opendev.org/c/openstack/ironic-python-agent/+/790471
|
||||
image_info['configdrive'] = configdrive
|
||||
# Now switch into the corresponding in-band deploy step and let the
|
||||
# result be polled normally.
|
||||
new_step = {'interface': 'deploy',
|
||||
|
@ -284,7 +284,7 @@ def _prepare_variables(task):
|
||||
image['checksum'] = 'md5:%s' % checksum
|
||||
_add_ssl_image_options(image)
|
||||
variables = {'image': image}
|
||||
configdrive = i_info.get('configdrive')
|
||||
configdrive = manager_utils.get_configdrive_image(task.node)
|
||||
if configdrive:
|
||||
if urlparse.urlparse(configdrive).scheme in ('http', 'https'):
|
||||
cfgdrv_type = 'url'
|
||||
|
@ -636,21 +636,12 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
||||
managers = redfish_utils.get_system(task.node).managers
|
||||
|
||||
deploy_info = _parse_deploy_info(node)
|
||||
configdrive = node.instance_info.get('configdrive')
|
||||
iso_ref = image_utils.prepare_boot_iso(task, deploy_info, **params)
|
||||
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
|
||||
_insert_vmedia(task, managers, iso_ref, sushy.VIRTUAL_MEDIA_CD)
|
||||
|
||||
if configdrive and boot_option == 'ramdisk':
|
||||
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
|
||||
cd_ref = image_utils.prepare_configdrive_image(task, configdrive)
|
||||
try:
|
||||
_insert_vmedia(task, managers, cd_ref,
|
||||
sushy.VIRTUAL_MEDIA_USBSTICK)
|
||||
except exception.InvalidParameterValue:
|
||||
raise exception.InstanceDeployFailure(
|
||||
_('Cannot attach configdrive for node %s: no suitable '
|
||||
'virtual USB slot has been found') % node.uuid)
|
||||
if boot_option == 'ramdisk':
|
||||
self._attach_configdrive(task, managers)
|
||||
|
||||
del managers
|
||||
|
||||
@ -660,6 +651,21 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
||||
"%(device)s", {'node': task.node.uuid,
|
||||
'device': boot_devices.CDROM})
|
||||
|
||||
def _attach_configdrive(self, task, managers):
|
||||
configdrive = manager_utils.get_configdrive_image(task.node)
|
||||
if not configdrive:
|
||||
return
|
||||
|
||||
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
|
||||
cd_ref = image_utils.prepare_configdrive_image(task, configdrive)
|
||||
try:
|
||||
_insert_vmedia(task, managers, cd_ref,
|
||||
sushy.VIRTUAL_MEDIA_USBSTICK)
|
||||
except exception.InvalidParameterValue:
|
||||
raise exception.InstanceDeployFailure(
|
||||
_('Cannot attach configdrive for node %s: no suitable '
|
||||
'virtual USB slot has been found') % task.node.uuid)
|
||||
|
||||
def _eject_all(self, task):
|
||||
managers = redfish_utils.get_system(task.node).managers
|
||||
|
||||
@ -676,7 +682,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
||||
|
||||
boot_option = deploy_utils.get_boot_option(task.node)
|
||||
if (boot_option == 'ramdisk'
|
||||
and task.node.instance_info.get('configdrive')):
|
||||
and task.node.instance_info.get('configdrive') is not None):
|
||||
_eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
|
||||
image_utils.cleanup_disk_image(task, prefix='configdrive')
|
||||
|
||||
|
@ -174,11 +174,12 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
d['driver_info'] = strutils.mask_dict_password(
|
||||
d.get('driver_info', {}), "******")
|
||||
iinfo = d.pop('instance_info', {})
|
||||
if not mask_configdrive:
|
||||
configdrive = iinfo.pop('configdrive', None)
|
||||
configdrive = iinfo.pop('configdrive', None)
|
||||
d['instance_info'] = strutils.mask_dict_password(iinfo, "******")
|
||||
if not mask_configdrive and configdrive:
|
||||
d['instance_info']['configdrive'] = configdrive
|
||||
if configdrive is not None:
|
||||
d['instance_info']['configdrive'] = (
|
||||
"******" if mask_configdrive else configdrive
|
||||
)
|
||||
d['driver_internal_info'] = strutils.mask_dict_password(
|
||||
d.get('driver_internal_info', {}), "******")
|
||||
return d
|
||||
|
@ -186,6 +186,25 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertNotIn('allocation_id', data)
|
||||
self.assertIn('allocation_uuid', data)
|
||||
|
||||
def test_get_one_configdrive_dict(self):
|
||||
fake_instance_info = {
|
||||
"configdrive": {'user_data': 'data'},
|
||||
"image_url": "http://example.com/test_image_url",
|
||||
"foo": "bar",
|
||||
}
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
chassis_id=self.chassis.id,
|
||||
instance_info=fake_instance_info)
|
||||
data = self.get_json(
|
||||
'/nodes/%s' % node.uuid,
|
||||
headers={api_base.Version.string: str(api_v1.max_version())})
|
||||
self.assertEqual(node.uuid, data['uuid'])
|
||||
self.assertEqual('******', data['driver_info']['fake_password'])
|
||||
self.assertEqual('bar', data['driver_info']['foo'])
|
||||
self.assertEqual('******', data['instance_info']['configdrive'])
|
||||
self.assertEqual('******', data['instance_info']['image_url'])
|
||||
self.assertEqual('bar', data['instance_info']['foo'])
|
||||
|
||||
def test_get_one_with_json(self):
|
||||
# Test backward compatibility with guess_content_type_from_ext
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
|
@ -174,63 +174,6 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
|
||||
def test__do_node_deploy_fast_track(self):
|
||||
self._test__do_node_deploy_ok(fast_track=True)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test__do_node_deploy_configdrive_as_dict(self, mock_cd):
|
||||
mock_cd.return_value = 'foo'
|
||||
configdrive = {'user_data': 'abcd'}
|
||||
self._test__do_node_deploy_ok(configdrive=configdrive,
|
||||
expected_configdrive='foo')
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=b'abcd',
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test__do_node_deploy_configdrive_as_dict_with_meta_data(self, mock_cd):
|
||||
mock_cd.return_value = 'foo'
|
||||
configdrive = {'meta_data': {'uuid': uuidutils.generate_uuid(),
|
||||
'name': 'new-name',
|
||||
'hostname': 'example.com'}}
|
||||
self._test__do_node_deploy_ok(configdrive=configdrive,
|
||||
expected_configdrive='foo')
|
||||
mock_cd.assert_called_once_with(configdrive['meta_data'],
|
||||
network_data=None,
|
||||
user_data=None,
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test__do_node_deploy_configdrive_with_network_data(self, mock_cd):
|
||||
mock_cd.return_value = 'foo'
|
||||
configdrive = {'network_data': {'links': []}}
|
||||
self._test__do_node_deploy_ok(configdrive=configdrive,
|
||||
expected_configdrive='foo')
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data={'links': []},
|
||||
user_data=None,
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test__do_node_deploy_configdrive_and_user_data_as_dict(self, mock_cd):
|
||||
mock_cd.return_value = 'foo'
|
||||
configdrive = {'user_data': {'user': 'data'}}
|
||||
self._test__do_node_deploy_ok(configdrive=configdrive,
|
||||
expected_configdrive='foo')
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=b'{"user": "data"}',
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test__do_node_deploy_configdrive_with_vendor_data(self, mock_cd):
|
||||
mock_cd.return_value = 'foo'
|
||||
configdrive = {'vendor_data': {'foo': 'bar'}}
|
||||
self._test__do_node_deploy_ok(configdrive=configdrive,
|
||||
expected_configdrive='foo')
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=None,
|
||||
vendor_data={'foo': 'bar'})
|
||||
|
||||
@mock.patch.object(swift, 'SwiftAPI', autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare',
|
||||
autospec=True)
|
||||
@ -1014,6 +957,37 @@ class StoreConfigDriveTestCase(db_base.DbTestCase):
|
||||
self.node.refresh()
|
||||
self.assertEqual(expected_instance_info, self.node.instance_info)
|
||||
|
||||
@mock.patch.object(conductor_utils, 'build_configdrive', autospec=True)
|
||||
def test_store_configdrive_swift_build(self, mock_cd, mock_swift):
|
||||
container_name = 'foo_container'
|
||||
timeout = 123
|
||||
expected_obj_name = 'configdrive-%s' % self.node.uuid
|
||||
expected_obj_header = {'X-Delete-After': str(timeout)}
|
||||
expected_instance_info = {'configdrive': 'http://1.2.3.4'}
|
||||
|
||||
mock_cd.return_value = 'fake'
|
||||
|
||||
# set configs and mocks
|
||||
CONF.set_override('configdrive_use_object_store', True,
|
||||
group='deploy')
|
||||
CONF.set_override('configdrive_swift_container', container_name,
|
||||
group='conductor')
|
||||
CONF.set_override('deploy_callback_timeout', timeout,
|
||||
group='conductor')
|
||||
mock_swift.return_value.get_temp_url.return_value = 'http://1.2.3.4'
|
||||
|
||||
deployments._store_configdrive(self.node, {'meta_data': {}})
|
||||
|
||||
mock_swift.assert_called_once_with()
|
||||
mock_swift.return_value.create_object.assert_called_once_with(
|
||||
container_name, expected_obj_name, mock.ANY,
|
||||
object_headers=expected_obj_header)
|
||||
mock_swift.return_value.get_temp_url.assert_called_once_with(
|
||||
container_name, expected_obj_name, timeout)
|
||||
self.node.refresh()
|
||||
self.assertEqual(expected_instance_info, self.node.instance_info)
|
||||
mock_cd.assert_called_once_with(self.node, {'meta_data': {}})
|
||||
|
||||
def test_store_configdrive_swift_no_deploy_timeout(self, mock_swift):
|
||||
container_name = 'foo_container'
|
||||
expected_obj_name = 'configdrive-%s' % self.node.uuid
|
||||
|
@ -2308,3 +2308,71 @@ class CacheVendorTestCase(db_base.DbTestCase):
|
||||
self.node.refresh()
|
||||
self.assertNotIn('vendor', self.node.properties)
|
||||
self.assertTrue(mock_log.called)
|
||||
|
||||
|
||||
class GetConfigDriveImageTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GetConfigDriveImageTestCase, self).setUp()
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
instance_info={})
|
||||
|
||||
def test_no_configdrive(self):
|
||||
self.assertIsNone(conductor_utils.get_configdrive_image(self.node))
|
||||
|
||||
def test_string(self):
|
||||
self.node.instance_info['configdrive'] = 'data'
|
||||
self.assertEqual('data',
|
||||
conductor_utils.get_configdrive_image(self.node))
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test_build_empty(self, mock_cd):
|
||||
self.node.instance_info['configdrive'] = {}
|
||||
self.assertEqual(mock_cd.return_value,
|
||||
conductor_utils.get_configdrive_image(self.node))
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=None,
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test_build_populated(self, mock_cd):
|
||||
configdrive = {
|
||||
'meta_data': {'uuid': uuidutils.generate_uuid(),
|
||||
'name': 'new-name',
|
||||
'hostname': 'example.com'},
|
||||
'network_data': {'links': []},
|
||||
'vendor_data': {'foo': 'bar'},
|
||||
}
|
||||
self.node.instance_info['configdrive'] = configdrive
|
||||
self.assertEqual(mock_cd.return_value,
|
||||
conductor_utils.get_configdrive_image(self.node))
|
||||
mock_cd.assert_called_once_with(
|
||||
configdrive['meta_data'],
|
||||
network_data=configdrive['network_data'],
|
||||
user_data=None,
|
||||
vendor_data=configdrive['vendor_data'])
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test_build_user_data_as_string(self, mock_cd):
|
||||
self.node.instance_info['configdrive'] = {'user_data': 'abcd'}
|
||||
self.assertEqual(mock_cd.return_value,
|
||||
conductor_utils.get_configdrive_image(self.node))
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=b'abcd',
|
||||
vendor_data=None)
|
||||
|
||||
@mock.patch('openstack.baremetal.configdrive.build', autospec=True)
|
||||
def test_build_user_data_as_dict(self, mock_cd):
|
||||
self.node.instance_info['configdrive'] = {
|
||||
'user_data': {'user': 'data'}
|
||||
}
|
||||
self.assertEqual(mock_cd.return_value,
|
||||
conductor_utils.get_configdrive_image(self.node))
|
||||
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
|
||||
network_data=None,
|
||||
user_data=b'{"user": "data"}',
|
||||
vendor_data=None)
|
||||
|
@ -495,6 +495,36 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase):
|
||||
mock.call().write('fake-content'),
|
||||
mock.call().__exit__(None, None, None)))
|
||||
|
||||
@mock.patch.object(utils, 'build_configdrive', autospec=True)
|
||||
def test__prepare_variables_configdrive_json(self, mock_build_configdrive):
|
||||
i_info = self.node.instance_info
|
||||
i_info['configdrive'] = {'meta_data': {}}
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
mock_build_configdrive.return_value = 'fake-content'
|
||||
configdrive_path = ('%(tempdir)s/%(node)s.cndrive' %
|
||||
{'tempdir': ansible_deploy.CONF.tempdir,
|
||||
'node': self.node.uuid})
|
||||
expected = {"image": {"url": "http://image",
|
||||
"validate_certs": "yes",
|
||||
"source": "fake-image",
|
||||
"disk_format": "qcow2",
|
||||
"checksum": "md5:checksum"},
|
||||
'configdrive': {'type': 'file',
|
||||
'location': configdrive_path}}
|
||||
with mock.patch.object(ansible_deploy, 'open', mock.mock_open(),
|
||||
create=True) as open_mock:
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertEqual(expected,
|
||||
ansible_deploy._prepare_variables(task))
|
||||
mock_build_configdrive.assert_called_once_with(
|
||||
task.node, {'meta_data': {}})
|
||||
open_mock.assert_has_calls((
|
||||
mock.call(configdrive_path, 'w'),
|
||||
mock.call().__enter__(),
|
||||
mock.call().write('fake-content'),
|
||||
mock.call().__exit__(None, None, None)))
|
||||
|
||||
def test__validate_clean_steps(self):
|
||||
steps = [{"interface": "deploy",
|
||||
"name": "foo",
|
||||
|
@ -849,15 +849,16 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'deploy_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'boot_mode_utils', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_prepare_instance_ramdisk_boot(
|
||||
self, mock_system, mock_boot_mode_utils, mock_deploy_utils,
|
||||
mock_manager_utils, mock__parse_deploy_info, mock__insert_vmedia,
|
||||
mock__eject_vmedia, mock_prepare_boot_iso, mock_prepare_disk,
|
||||
mock_clean_up_instance):
|
||||
mock_node_set_boot_device, mock__parse_deploy_info,
|
||||
mock__insert_vmedia, mock__eject_vmedia, mock_prepare_boot_iso,
|
||||
mock_prepare_disk, mock_clean_up_instance):
|
||||
|
||||
configdrive = 'Y29udGVudA=='
|
||||
managers = mock_system.return_value.managers
|
||||
@ -899,7 +900,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
'cd-url', sushy.VIRTUAL_MEDIA_USBSTICK),
|
||||
])
|
||||
|
||||
mock_manager_utils.node_set_boot_device.assert_called_once_with(
|
||||
mock_node_set_boot_device.assert_called_once_with(
|
||||
task, boot_devices.CDROM, persistent=True)
|
||||
|
||||
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
|
||||
@ -910,14 +911,16 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'deploy_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'boot_mode_utils', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_prepare_instance_ramdisk_boot_iso(
|
||||
self, mock_system, mock_boot_mode_utils, mock_deploy_utils,
|
||||
mock_manager_utils, mock__parse_deploy_info, mock__insert_vmedia,
|
||||
mock__eject_vmedia, mock_prepare_boot_iso, mock_clean_up_instance):
|
||||
mock_node_set_boot_device, mock__parse_deploy_info,
|
||||
mock__insert_vmedia, mock__eject_vmedia, mock_prepare_boot_iso,
|
||||
mock_clean_up_instance):
|
||||
|
||||
managers = mock_system.return_value.managers
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
@ -948,7 +951,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
mock__insert_vmedia.assert_called_once_with(
|
||||
task, managers, 'image-url', sushy.VIRTUAL_MEDIA_CD)
|
||||
|
||||
mock_manager_utils.node_set_boot_device.assert_called_once_with(
|
||||
mock_node_set_boot_device.assert_called_once_with(
|
||||
task, boot_devices.CDROM, persistent=True)
|
||||
|
||||
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
|
||||
@ -959,14 +962,16 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'deploy_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'boot_mode_utils', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_prepare_instance_ramdisk_boot_iso_boot(
|
||||
self, mock_system, mock_boot_mode_utils, mock_deploy_utils,
|
||||
mock_manager_utils, mock__parse_deploy_info, mock__insert_vmedia,
|
||||
mock__eject_vmedia, mock_prepare_boot_iso, mock_clean_up_instance):
|
||||
mock_node_set_boot_device, mock__parse_deploy_info,
|
||||
mock__insert_vmedia, mock__eject_vmedia, mock_prepare_boot_iso,
|
||||
mock_clean_up_instance):
|
||||
|
||||
managers = mock_system.return_value.managers
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
@ -991,7 +996,76 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
||||
mock__insert_vmedia.assert_called_once_with(
|
||||
task, managers, 'image-url', sushy.VIRTUAL_MEDIA_CD)
|
||||
|
||||
mock_manager_utils.node_set_boot_device.assert_called_once_with(
|
||||
mock_node_set_boot_device.assert_called_once_with(
|
||||
task, boot_devices.CDROM, persistent=True)
|
||||
|
||||
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(redfish_boot.manager_utils, 'build_configdrive',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
|
||||
'_eject_all', autospec=True)
|
||||
@mock.patch.object(image_utils, 'prepare_configdrive_image', autospec=True)
|
||||
@mock.patch.object(image_utils, 'prepare_boot_iso', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
|
||||
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
|
||||
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'deploy_utils', autospec=True)
|
||||
@mock.patch.object(redfish_boot, 'boot_mode_utils', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_prepare_instance_ramdisk_boot_render_configdrive(
|
||||
self, mock_system, mock_boot_mode_utils, mock_deploy_utils,
|
||||
mock_node_set_boot_device, mock__parse_deploy_info,
|
||||
mock__insert_vmedia, mock__eject_vmedia, mock_prepare_boot_iso,
|
||||
mock_prepare_disk, mock_clean_up_instance, mock_build_configdrive):
|
||||
|
||||
configdrive = 'Y29udGVudA=='
|
||||
managers = mock_system.return_value.managers
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
task.node.driver_internal_info[
|
||||
'root_uuid_or_disk_id'] = self.node.uuid
|
||||
task.node.instance_info['configdrive'] = {'meta_data': {}}
|
||||
|
||||
mock_build_configdrive.return_value = configdrive
|
||||
|
||||
mock_deploy_utils.get_boot_option.return_value = 'ramdisk'
|
||||
|
||||
d_info = {
|
||||
'deploy_kernel': 'kernel',
|
||||
'deploy_ramdisk': 'ramdisk',
|
||||
'bootloader': 'bootloader'
|
||||
}
|
||||
mock__parse_deploy_info.return_value = d_info
|
||||
|
||||
mock_prepare_boot_iso.return_value = 'image-url'
|
||||
mock_prepare_disk.return_value = 'cd-url'
|
||||
|
||||
task.driver.boot.prepare_instance(task)
|
||||
|
||||
mock_clean_up_instance.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
mock_build_configdrive.assert_called_once_with(
|
||||
task.node, {'meta_data': {}})
|
||||
mock_prepare_boot_iso.assert_called_once_with(task, d_info)
|
||||
mock_prepare_disk.assert_called_once_with(task, configdrive)
|
||||
|
||||
mock__eject_vmedia.assert_has_calls([
|
||||
mock.call(task, managers, sushy.VIRTUAL_MEDIA_CD),
|
||||
mock.call(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK),
|
||||
])
|
||||
|
||||
mock__insert_vmedia.assert_has_calls([
|
||||
mock.call(task, managers,
|
||||
'image-url', sushy.VIRTUAL_MEDIA_CD),
|
||||
mock.call(task, managers,
|
||||
'cd-url', sushy.VIRTUAL_MEDIA_USBSTICK),
|
||||
])
|
||||
|
||||
mock_node_set_boot_device.assert_called_once_with(
|
||||
task, boot_devices.CDROM, persistent=True)
|
||||
|
||||
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
|
||||
|
@ -1482,6 +1482,73 @@ class TestAgentDeploy(CommonTestsMixin, db_base.DbTestCase):
|
||||
self.assertEqual(states.ACTIVE,
|
||||
task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(manager_utils, 'build_configdrive', autospec=True)
|
||||
def test_write_image_render_configdrive(self, mock_build_configdrive):
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
i_info = self.node.instance_info
|
||||
i_info['kernel'] = 'kernel'
|
||||
i_info['ramdisk'] = 'ramdisk'
|
||||
i_info['root_gb'] = 10
|
||||
i_info['swap_mb'] = 10
|
||||
i_info['ephemeral_mb'] = 0
|
||||
i_info['ephemeral_format'] = 'abc'
|
||||
i_info['configdrive'] = {'meta_data': {}}
|
||||
i_info['preserve_ephemeral'] = False
|
||||
i_info['image_type'] = 'partition'
|
||||
i_info['root_mb'] = 10240
|
||||
i_info['deploy_boot_mode'] = 'bios'
|
||||
i_info['capabilities'] = {"boot_option": "local",
|
||||
"disk_label": "msdos"}
|
||||
self.node.instance_info = i_info
|
||||
driver_internal_info = self.node.driver_internal_info
|
||||
driver_internal_info['is_whole_disk_image'] = False
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
self.node.save()
|
||||
test_temp_url = 'http://image'
|
||||
expected_image_info = {
|
||||
'urls': [test_temp_url],
|
||||
'id': 'fake-image',
|
||||
'node_uuid': self.node.uuid,
|
||||
'checksum': 'checksum',
|
||||
'disk_format': 'qcow2',
|
||||
'container_format': 'bare',
|
||||
'stream_raw_images': True,
|
||||
'kernel': 'kernel',
|
||||
'ramdisk': 'ramdisk',
|
||||
'root_gb': 10,
|
||||
'swap_mb': 10,
|
||||
'ephemeral_mb': 0,
|
||||
'ephemeral_format': 'abc',
|
||||
'configdrive': 'configdrive',
|
||||
'preserve_ephemeral': False,
|
||||
'image_type': 'partition',
|
||||
'root_mb': 10240,
|
||||
'boot_option': 'local',
|
||||
'deploy_boot_mode': 'bios',
|
||||
'disk_label': 'msdos'
|
||||
}
|
||||
|
||||
mock_build_configdrive.return_value = 'configdrive'
|
||||
|
||||
client_mock = mock.MagicMock(spec_set=['execute_deploy_step'])
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy._client = client_mock
|
||||
task.driver.deploy.write_image(task)
|
||||
|
||||
step = {'step': 'write_image', 'interface': 'deploy',
|
||||
'args': {'image_info': expected_image_info,
|
||||
'configdrive': 'configdrive'}}
|
||||
client_mock.execute_deploy_step.assert_called_once_with(
|
||||
step, task.node, mock.ANY)
|
||||
self.assertEqual(states.DEPLOYWAIT, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE,
|
||||
task.node.target_provision_state)
|
||||
mock_build_configdrive.assert_called_once_with(
|
||||
task.node, {'meta_data': {}})
|
||||
|
||||
@mock.patch.object(deploy_utils, 'remove_http_instance_symlink',
|
||||
autospec=True)
|
||||
@mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
|
||||
|
@ -61,6 +61,18 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
||||
# Ensure the node can be serialised.
|
||||
jsonutils.dumps(d)
|
||||
|
||||
def test_as_dict_secure_configdrive_as_dict(self):
|
||||
self.node.driver_info['ipmi_password'] = 'fake'
|
||||
self.node.instance_info['configdrive'] = {'user_data': 'data'}
|
||||
self.node.driver_internal_info['agent_secret_token'] = 'abc'
|
||||
d = self.node.as_dict(secure=True)
|
||||
self.assertEqual('******', d['driver_info']['ipmi_password'])
|
||||
self.assertEqual('******', d['instance_info']['configdrive'])
|
||||
self.assertEqual('******',
|
||||
d['driver_internal_info']['agent_secret_token'])
|
||||
# Ensure the node can be serialised.
|
||||
jsonutils.dumps(d)
|
||||
|
||||
def test_as_dict_secure_with_configdrive(self):
|
||||
self.node.driver_info['ipmi_password'] = 'fake'
|
||||
self.node.instance_info['configdrive'] = 'data'
|
||||
@ -73,6 +85,19 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
||||
# Ensure the node can be serialised.
|
||||
jsonutils.dumps(d)
|
||||
|
||||
def test_as_dict_secure_with_configdrive_as_dict(self):
|
||||
self.node.driver_info['ipmi_password'] = 'fake'
|
||||
self.node.instance_info['configdrive'] = {'user_data': 'data'}
|
||||
self.node.driver_internal_info['agent_secret_token'] = 'abc'
|
||||
d = self.node.as_dict(secure=True, mask_configdrive=False)
|
||||
self.assertEqual('******', d['driver_info']['ipmi_password'])
|
||||
self.assertEqual({'user_data': 'data'},
|
||||
d['instance_info']['configdrive'])
|
||||
self.assertEqual('******',
|
||||
d['driver_internal_info']['agent_secret_token'])
|
||||
# Ensure the node can be serialised.
|
||||
jsonutils.dumps(d)
|
||||
|
||||
def test_as_dict_with_traits(self):
|
||||
self.fake_node['traits'] = ['CUSTOM_1']
|
||||
self.node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
other:
|
||||
- |
|
||||
Configuration drives are now stored in their JSON representation and only
|
||||
rendered when needed. This allows deploy steps to access the original
|
||||
JSON representation rather than only the rendered ISO image.
|
Loading…
Reference in New Issue
Block a user