Merge "Adds ramdisk deploy driver"
This commit is contained in:
commit
5b79f25027
@ -47,7 +47,7 @@ class GenericHardware(hardware_type.AbstractHardwareType):
|
||||
def supported_deploy_interfaces(self):
|
||||
"""List of supported deploy interfaces."""
|
||||
return [iscsi_deploy.ISCSIDeploy, agent.AgentDeploy,
|
||||
ansible_deploy.AnsibleDeploy]
|
||||
ansible_deploy.AnsibleDeploy, pxe.PXERamdiskDeploy]
|
||||
|
||||
@property
|
||||
def supported_inspect_interfaces(self):
|
||||
|
@ -58,7 +58,7 @@ LOG = logging.getLogger(__name__)
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
SUPPORTED_CAPABILITIES = {
|
||||
'boot_option': ('local', 'netboot'),
|
||||
'boot_option': ('local', 'netboot', 'ramdisk'),
|
||||
'boot_mode': ('bios', 'uefi'),
|
||||
'secure_boot': ('true', 'false'),
|
||||
'trusted_boot': ('true', 'false'),
|
||||
@ -284,13 +284,16 @@ def _replace_root_uuid(path, root_uuid):
|
||||
|
||||
|
||||
def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
||||
trusted_boot=False, iscsi_boot=False):
|
||||
trusted_boot=False, iscsi_boot=False,
|
||||
ramdisk_boot=False):
|
||||
if is_whole_disk_image:
|
||||
boot_disk_type = 'boot_whole_disk'
|
||||
elif trusted_boot:
|
||||
boot_disk_type = 'trusted_boot'
|
||||
elif iscsi_boot:
|
||||
boot_disk_type = 'boot_iscsi'
|
||||
elif ramdisk_boot:
|
||||
boot_disk_type = 'boot_ramdisk'
|
||||
else:
|
||||
boot_disk_type = 'boot_partition'
|
||||
|
||||
@ -312,7 +315,7 @@ def _replace_disk_identifier(path, disk_identifier):
|
||||
|
||||
def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
||||
is_whole_disk_image, trusted_boot=False,
|
||||
iscsi_boot=False):
|
||||
iscsi_boot=False, ramdisk_boot=False):
|
||||
"""Switch a pxe config from deployment mode to service mode.
|
||||
|
||||
:param path: path to the pxe config file in tftpboot.
|
||||
@ -324,14 +327,16 @@ def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
||||
is_whole_disk_image and trusted_boot are mutually exclusive. You can
|
||||
have one or neither, but not both.
|
||||
:param iscsi_boot: if boot is from an iSCSI volume or not.
|
||||
:param ramdisk_boot: if the boot is to be to a ramdisk configuration.
|
||||
"""
|
||||
if not is_whole_disk_image:
|
||||
_replace_root_uuid(path, root_uuid_or_disk_id)
|
||||
else:
|
||||
_replace_disk_identifier(path, root_uuid_or_disk_id)
|
||||
if not ramdisk_boot:
|
||||
if not is_whole_disk_image:
|
||||
_replace_root_uuid(path, root_uuid_or_disk_id)
|
||||
else:
|
||||
_replace_disk_identifier(path, root_uuid_or_disk_id)
|
||||
|
||||
_replace_boot_line(path, boot_mode, is_whole_disk_image, trusted_boot,
|
||||
iscsi_boot)
|
||||
iscsi_boot, ramdisk_boot)
|
||||
|
||||
|
||||
def get_dev(address, port, iqn, lun):
|
||||
@ -365,7 +370,8 @@ def deploy_partition_image(
|
||||
partition table has not changed).
|
||||
:param configdrive: Optional. Base64 encoded Gzipped configdrive content
|
||||
or configdrive HTTP URL.
|
||||
:param boot_option: Can be "local" or "netboot". "netboot" by default.
|
||||
:param boot_option: Can be "local" or "netboot", or "ramdisk".
|
||||
"netboot" by default.
|
||||
:param boot_mode: Can be "bios" or "uefi". "bios" by default.
|
||||
:param disk_label: The disk label to be used when creating the
|
||||
partition table. Valid values are: "msdos", "gpt" or None; If None
|
||||
|
@ -30,6 +30,12 @@ imgfree
|
||||
kernel {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.aki_path }} root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }} initrd=ramdisk || goto boot_partition
|
||||
initrd {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.ari_path }} || goto boot_partition
|
||||
boot
|
||||
|
||||
:boot_ramdisk
|
||||
imgfree
|
||||
kernel {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.aki_path }} root=/dev/ram0 text {{ pxe_options.pxe_append_params|default("", true) }} {{ pxe_options.ramdisk_opts|default('', true) }} initrd=ramdisk || goto boot_ramdisk
|
||||
initrd {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.ari_path }} || goto boot_ramdisk
|
||||
boot
|
||||
{%- if pxe_options.boot_from_volume %}
|
||||
|
||||
:boot_iscsi
|
||||
|
@ -35,6 +35,7 @@ from ironic.common import states
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import boot_mode_utils
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules import image_cache
|
||||
@ -211,6 +212,18 @@ def _build_instance_pxe_options(task, pxe_info):
|
||||
pxe_opts.setdefault('aki_path', 'no_kernel')
|
||||
pxe_opts.setdefault('ari_path', 'no_ramdisk')
|
||||
|
||||
# TODO(TheJulia): We should only do this if we have a ramdisk interface.
|
||||
# We should check the capabilities of the class, but that becomes a bit
|
||||
# of a pain for unit testing. We can sort this out in Stein since we will
|
||||
# need to revisit a major portion of this file to effetively begin the
|
||||
# ipxe boot interface promotion.
|
||||
if isinstance(task.driver.deploy, PXERamdiskDeploy):
|
||||
i_info = task.node.instance_info
|
||||
try:
|
||||
pxe_opts['ramdisk_opts'] = i_info['ramdisk_kernel_arguments']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return pxe_opts
|
||||
|
||||
|
||||
@ -266,7 +279,8 @@ def _build_pxe_config_options(task, pxe_info, service=False):
|
||||
|
||||
|
||||
def _build_service_pxe_config(task, instance_image_info,
|
||||
root_uuid_or_disk_id):
|
||||
root_uuid_or_disk_id,
|
||||
ramdisk_boot=False):
|
||||
node = task.node
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
|
||||
# NOTE(pas-ha) if it is takeover of ACTIVE node or node performing
|
||||
@ -283,7 +297,7 @@ def _build_service_pxe_config(task, instance_image_info,
|
||||
pxe_config_path, root_uuid_or_disk_id,
|
||||
boot_mode_utils.get_boot_mode_for_deploy(node),
|
||||
iwdi, deploy_utils.is_trusted_boot_requested(node),
|
||||
deploy_utils.is_iscsi_boot(task))
|
||||
deploy_utils.is_iscsi_boot(task), ramdisk_boot)
|
||||
|
||||
|
||||
def _get_volume_pxe_options(task):
|
||||
@ -417,7 +431,7 @@ def _clean_up_pxe_env(task, images_info):
|
||||
|
||||
class PXEBoot(base.BootInterface):
|
||||
|
||||
capabilities = ['iscsi_volume_boot']
|
||||
capabilities = ['iscsi_volume_boot', 'ramdisk_boot']
|
||||
|
||||
def __init__(self):
|
||||
if CONF.pxe.ipxe_enabled:
|
||||
@ -597,7 +611,6 @@ class PXEBoot(base.BootInterface):
|
||||
node = task.node
|
||||
boot_option = deploy_utils.get_boot_option(node)
|
||||
boot_device = None
|
||||
|
||||
if deploy_utils.is_iscsi_boot(task):
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
|
||||
provider = dhcp_factory.DHCPFactory()
|
||||
@ -618,6 +631,22 @@ class PXEBoot(base.BootInterface):
|
||||
iscsi_boot=True)
|
||||
boot_device = boot_devices.PXE
|
||||
|
||||
elif boot_option == "ramdisk":
|
||||
instance_image_info = _get_instance_image_info(
|
||||
task.node, task.context)
|
||||
_cache_ramdisk_kernel(task.context, task.node,
|
||||
instance_image_info)
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
|
||||
provider = dhcp_factory.DHCPFactory()
|
||||
provider.update_dhcp(task, dhcp_opts)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
deploy_utils.switch_pxe_config(
|
||||
pxe_config_path, None,
|
||||
boot_mode_utils.get_boot_mode_for_deploy(node), False,
|
||||
iscsi_boot=False, ramdisk_boot=True)
|
||||
boot_device = boot_devices.PXE
|
||||
|
||||
elif boot_option != "local":
|
||||
if task.driver.storage.should_write_image(task):
|
||||
# Make sure that the instance kernel/ramdisk is cached.
|
||||
@ -702,3 +731,79 @@ class PXEBoot(base.BootInterface):
|
||||
parameters
|
||||
"""
|
||||
_parse_driver_info(task.node, mode='rescue')
|
||||
|
||||
|
||||
class PXERamdiskDeploy(agent.AgentDeploy, agent.AgentDeployMixin,
|
||||
base.DeployInterface):
|
||||
|
||||
def validate(self, task):
|
||||
# Initially this is likely okay, we can iterate on this and
|
||||
# enable other drivers that have similar functionality that
|
||||
# be invoked in a ramdisk friendly way.
|
||||
if not isinstance(task.driver.boot, PXEBoot):
|
||||
raise exception.InvalidParameterValue(
|
||||
err=('Invalid configuration: The ramdisk deploy '
|
||||
'interface requires the pxe boot interface.'))
|
||||
# Eventually we should be doing this.
|
||||
if 'ramdisk_boot' not in task.driver.boot.capabilities:
|
||||
raise exception.InvalidParameterValue(
|
||||
err=('Invalid configuration: The boot interface '
|
||||
'must have the `ramdisk_boot` capability. '
|
||||
'Not found.'))
|
||||
task.driver.boot.validate(task)
|
||||
|
||||
# Validate node capabilities
|
||||
deploy_utils.validate_capabilities(task.node)
|
||||
|
||||
def deploy(self, task):
|
||||
if 'configdrive' in task.node.instance_info:
|
||||
LOG.warning('A configuration drive is present with '
|
||||
'in the deployment request of node %(node)s. '
|
||||
'The configuration drive will be ignored for '
|
||||
'this deployment.',
|
||||
{'node': task.node})
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
# Tenant neworks must enable connectivity to the boot
|
||||
# location, as reboot() can otherwise be very problematic.
|
||||
# IDEA(TheJulia): Maybe a "trusted environment" mode flag
|
||||
# that we otherwise fail validation on for drivers that
|
||||
# require explicit security postures.
|
||||
task.driver.network.configure_tenant_networks(task)
|
||||
|
||||
# calling boot.prepare_instance will also set the node
|
||||
# to PXE boot, and update PXE templates accordingly
|
||||
task.driver.boot.prepare_instance(task)
|
||||
|
||||
# Power-on the instance, with PXE prepared, we're done.
|
||||
manager_utils.node_power_action(task, states.POWER_ON)
|
||||
LOG.info('Deployment setup for node %s done', task.node.uuid)
|
||||
# TODO(TheJulia): Update this in stein to support deploy steps.
|
||||
return states.DEPLOYDONE
|
||||
|
||||
def prepare(self, task):
|
||||
node = task.node
|
||||
# Log a warning if the boot_option is wrong... and
|
||||
# otherwise reset it.
|
||||
if deploy_utils.get_boot_option(node) != 'ramdisk':
|
||||
LOG.warning('Incorrect "boot_option" set for node %(node)s '
|
||||
'and will be overridden to "ramdisk" as the '
|
||||
'to match the deploy interface.',
|
||||
{'node': node.uuid})
|
||||
i_info = task.node.instance_info
|
||||
i_info.update({'capabilities': {'boot_option': 'ramdisk'}})
|
||||
node.instance_info = i_info
|
||||
node.save()
|
||||
|
||||
deploy_utils.populate_storage_driver_internal_info(task)
|
||||
if node.provision_state == states.DEPLOYING:
|
||||
# Ask the network interface to validate itself so
|
||||
# we can ensure we are able to proceed.
|
||||
task.driver.network.validate(task)
|
||||
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
# NOTE(TheJulia): If this was any other interface, we would
|
||||
# unconfigure tenant networks, add provisioning networks, etc.
|
||||
task.driver.storage.attach_volumes(task)
|
||||
if node.provision_state in (states.ACTIVE, states.UNRESCUING):
|
||||
# In the event of takeover or unrescue.
|
||||
task.driver.boot.prepare_instance(task)
|
||||
|
@ -18,3 +18,7 @@ append mbr:{{ DISK_IDENTIFIER }}
|
||||
label trusted_boot
|
||||
kernel mboot
|
||||
append tboot.gz --- {{pxe_options.aki_path}} root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }} intel_iommu=on --- {{pxe_options.ari_path}}
|
||||
|
||||
label boot_ramdisk
|
||||
kernel {{ pxe_options.aki_path }}
|
||||
append initrd={{ pxe_options.ari_path }} root=/dev/ram0 text {{ pxe_options.pxe_append_params|default("", true) }} {{ pxe_options.ramdisk_opts|default('', true) }}
|
||||
|
@ -12,6 +12,11 @@ menuentry "boot_partition" {
|
||||
initrdefi {{ pxe_options.ari_path }}
|
||||
}
|
||||
|
||||
menuentry "boot_ramdisk" {
|
||||
linuxefi {{ pxe_options.deployment_aki_path }} root=/dev/ram0 text {{ pxe_options.pxe_append_params|default("", true) }} {{ pxe_options.ramdisk_opts|default('', true) }}
|
||||
initrdefi {{ pxe_options.deployment_ari_path }}
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:{{ DISK_IDENTIFIER }}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
u'f33c123/deploy_ramdisk',
|
||||
'ipa-api-url': 'http://192.168.122.184:6385',
|
||||
'ipxe_timeout': 0,
|
||||
'ramdisk_opts': 'ramdisk_param',
|
||||
}
|
||||
|
||||
self.ipxe_options = self.pxe_options.copy()
|
||||
|
@ -31,5 +31,11 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||
boot
|
||||
|
||||
:boot_ramdisk
|
||||
imgfree
|
||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_ramdisk
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
sanboot --no-describe
|
||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||
boot
|
||||
|
||||
:boot_ramdisk
|
||||
imgfree
|
||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_ramdisk
|
||||
boot
|
||||
|
||||
:boot_iscsi
|
||||
imgfree
|
||||
set username fake_username
|
||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||
boot
|
||||
|
||||
:boot_ramdisk
|
||||
imgfree
|
||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_ramdisk
|
||||
boot
|
||||
|
||||
:boot_iscsi
|
||||
imgfree
|
||||
set username fake_username
|
||||
|
@ -31,5 +31,11 @@ kernel --timeout 120 http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_par
|
||||
initrd --timeout 120 http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||
boot
|
||||
|
||||
:boot_ramdisk
|
||||
imgfree
|
||||
kernel --timeout 120 http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||
initrd --timeout 120 http://1.2.3.4:1234/ramdisk || goto boot_ramdisk
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
sanboot --no-describe
|
||||
|
@ -1420,7 +1420,7 @@ class ParseInstanceInfoCapabilitiesTestCase(tests_base.TestCase):
|
||||
utils.validate_capabilities, self.node)
|
||||
|
||||
def test_all_supported_capabilities(self):
|
||||
self.assertEqual(('local', 'netboot'),
|
||||
self.assertEqual(('local', 'netboot', 'ramdisk'),
|
||||
utils.SUPPORTED_CAPABILITIES['boot_option'])
|
||||
self.assertEqual(('bios', 'uefi'),
|
||||
utils.SUPPORTED_CAPABILITIES['boot_mode'])
|
||||
|
@ -1254,7 +1254,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||
switch_pxe_config_mock.assert_called_once_with(
|
||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||
'bios', False, False, False)
|
||||
'bios', False, False, False, False)
|
||||
set_boot_device_mock.assert_called_once_with(task,
|
||||
boot_devices.PXE,
|
||||
persistent=True)
|
||||
@ -1297,7 +1297,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
task, mock.ANY, CONF.pxe.pxe_config_template)
|
||||
switch_pxe_config_mock.assert_called_once_with(
|
||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||
'bios', False, False, False)
|
||||
'bios', False, False, False, False)
|
||||
self.assertFalse(set_boot_device_mock.called)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||
@ -1467,6 +1467,180 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
task.node, task.context)
|
||||
|
||||
|
||||
class PXEBootDeployTestCase(db_base.DbTestCase):
|
||||
|
||||
driver = 'fake-hardware'
|
||||
|
||||
def setUp(self):
|
||||
super(PXEBootDeployTestCase, self).setUp()
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.config(tftp_root=self.temp_dir, group='pxe')
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.config(images_path=self.temp_dir, group='pxe')
|
||||
self.config(enabled_deploy_interfaces=['ramdisk'])
|
||||
self.config(enabled_boot_interfaces=['pxe'])
|
||||
for iface in drivers_base.ALL_INTERFACES:
|
||||
impl = 'fake'
|
||||
if iface == 'network':
|
||||
impl = 'noop'
|
||||
if iface == 'deploy':
|
||||
impl = 'ramdisk'
|
||||
if iface == 'boot':
|
||||
impl = 'pxe'
|
||||
config_kwarg = {'enabled_%s_interfaces' % iface: [impl],
|
||||
'default_%s_interface' % iface: impl}
|
||||
self.config(**config_kwarg)
|
||||
self.config(enabled_hardware_types=[self.driver])
|
||||
instance_info = INST_INFO_DICT
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver=self.driver,
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT)
|
||||
self.port = obj_utils.create_test_port(self.context,
|
||||
node_id=self.node.id)
|
||||
self.config(group='conductor', api_url='http://127.0.0.1:1234/')
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True)
|
||||
@mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True)
|
||||
@mock.patch.object(pxe, '_cache_ramdisk_kernel', autospec=True)
|
||||
@mock.patch.object(pxe, '_get_instance_image_info', autospec=True)
|
||||
def test_prepare_instance_ramdisk(
|
||||
self, get_image_info_mock, cache_mock,
|
||||
dhcp_factory_mock, switch_pxe_config_mock,
|
||||
set_boot_device_mock):
|
||||
provider_mock = mock.MagicMock()
|
||||
dhcp_factory_mock.return_value = provider_mock
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
image_info = {'kernel': ('', '/path/to/kernel'),
|
||||
'ramdisk': ('', '/path/to/ramdisk')}
|
||||
get_image_info_mock.return_value = image_info
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
task.node.properties['capabilities'] = 'boot_option:netboot'
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
task.driver.deploy.prepare(task)
|
||||
task.driver.deploy.deploy(task)
|
||||
|
||||
get_image_info_mock.assert_called_once_with(
|
||||
task.node, task.context)
|
||||
cache_mock.assert_called_once_with(
|
||||
task.context, task.node, image_info)
|
||||
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||
switch_pxe_config_mock.assert_called_once_with(
|
||||
pxe_config_path, None,
|
||||
'bios', False, iscsi_boot=False, ramdisk_boot=True)
|
||||
set_boot_device_mock.assert_called_once_with(task,
|
||||
boot_devices.PXE,
|
||||
persistent=True)
|
||||
|
||||
@mock.patch.object(pxe.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True)
|
||||
@mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True)
|
||||
@mock.patch.object(pxe, '_cache_ramdisk_kernel', autospec=True)
|
||||
@mock.patch.object(pxe, '_get_instance_image_info', autospec=True)
|
||||
def test_deploy(self, mock_image_info, mock_cache,
|
||||
mock_dhcp_factory, mock_switch_config, mock_warning):
|
||||
image_info = {'kernel': ('', '/path/to/kernel'),
|
||||
'ramdisk': ('', '/path/to/ramdisk')}
|
||||
mock_image_info.return_value = image_info
|
||||
i_info = self.node.instance_info
|
||||
i_info.update({'capabilities': {'boot_option': 'ramdisk'}})
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertEqual(states.DEPLOYDONE,
|
||||
task.driver.deploy.deploy(task))
|
||||
mock_image_info.assert_called_once_with(
|
||||
task.node, task.context)
|
||||
mock_cache.assert_called_once_with(
|
||||
task.context, task.node, image_info)
|
||||
self.assertFalse(mock_warning.called)
|
||||
i_info['configdrive'] = 'meow'
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
mock_warning.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertEqual(states.DEPLOYDONE,
|
||||
task.driver.deploy.deploy(task))
|
||||
self.assertTrue(mock_warning.called)
|
||||
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
def test_prepare(self, mock_prepare_instance):
|
||||
node = self.node
|
||||
node.provision_state = states.DEPLOYING
|
||||
node.instance_info = {}
|
||||
node.save()
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
self.assertFalse(mock_prepare_instance.called)
|
||||
self.assertEqual({'boot_option': 'ramdisk'},
|
||||
task.node.instance_info['capabilities'])
|
||||
|
||||
node.provision_state = states.ACTIVE
|
||||
node.save()
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
mock_prepare_instance.assert_called_once_with(mock.ANY, task)
|
||||
mock_prepare_instance.reset_mock()
|
||||
|
||||
node.provision_state = states.UNRESCUING
|
||||
node.save()
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
mock_prepare_instance.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
@mock.patch.object(pxe.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
def test_prepare_fixes_and_logs_boot_option_warning(
|
||||
self, mock_prepare_instance, mock_warning):
|
||||
node = self.node
|
||||
node.properties['capabilities'] = 'boot_option:ramdisk'
|
||||
node.provision_state = states.DEPLOYING
|
||||
node.instance_info = {}
|
||||
node.save()
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
self.assertFalse(mock_prepare_instance.called)
|
||||
self.assertEqual({'boot_option': 'ramdisk'},
|
||||
task.node.instance_info['capabilities'])
|
||||
self.assertTrue(mock_warning.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'validate_image_properties',
|
||||
autospec=True)
|
||||
def test_validate(self, mock_validate_img):
|
||||
node = self.node
|
||||
node.properties['capabilities'] = 'boot_option:netboot'
|
||||
node.save()
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
self.assertTrue(mock_validate_img.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'validate_image_properties',
|
||||
autospec=True)
|
||||
def test_validate_interface_mismatch(self, mock_validate_image):
|
||||
node = self.node
|
||||
node.boot_interface = 'fake'
|
||||
node.save()
|
||||
self.config(enabled_boot_interfaces=['fake'],
|
||||
default_boot_interface='fake')
|
||||
with task_manager.acquire(self.context, node.uuid) as task:
|
||||
self.assertRaisesRegexp(exception.InvalidParameterValue,
|
||||
'requires the pxe boot interface',
|
||||
task.driver.deploy.validate, task)
|
||||
self.assertFalse(mock_validate_image.called)
|
||||
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_calls_boot_validate(self, mock_validate):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
mock_validate.assert_called_once_with(mock.ANY, task)
|
||||
|
||||
|
||||
class PXEValidateRescueTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -18,3 +18,7 @@ append mbr:{{ DISK_IDENTIFIER }}
|
||||
label trusted_boot
|
||||
kernel mboot
|
||||
append tboot.gz --- /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel root={{ ROOT }} ro text test_param intel_iommu=on --- /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk
|
||||
|
||||
label boot_ramdisk
|
||||
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel
|
||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk root=/dev/ram0 text test_param ramdisk_param
|
||||
|
@ -12,6 +12,11 @@ menuentry "boot_partition" {
|
||||
initrdefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_ramdisk" {
|
||||
linuxefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_kernel root=/dev/ram0 text test_param ramdisk_param
|
||||
initrdefi /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk
|
||||
}
|
||||
|
||||
menuentry "boot_whole_disk" {
|
||||
linuxefi chain.c32 mbr:(( DISK_IDENTIFIER ))
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds a ``ramdisk`` deploy interface for deployments that wish to network
|
||||
boot to a ramdisk, as opposed to perform a complete
|
||||
traditional deployment to a physical media. This may be useful in
|
||||
scientific use cases or where ephemeral baremetal machines are desired.
|
||||
|
||||
The ``ramdisk`` deploy interface is intended for advanced users and has
|
||||
some particular operational caveats that the users should be aware of
|
||||
prior to use, such as network access list requirements and configuration
|
||||
drive architectural restrictions and the inability to leverage
|
||||
configuration drives.
|
@ -80,6 +80,7 @@ ironic.hardware.interfaces.deploy =
|
||||
iscsi = ironic.drivers.modules.iscsi_deploy:ISCSIDeploy
|
||||
oneview-direct = ironic.drivers.modules.oneview.deploy:OneViewAgentDeploy
|
||||
oneview-iscsi = ironic.drivers.modules.oneview.deploy:OneViewIscsiDeploy
|
||||
ramdisk = ironic.drivers.modules.pxe:PXERamdiskDeploy
|
||||
|
||||
ironic.hardware.interfaces.inspect =
|
||||
fake = ironic.drivers.modules.fake:FakeInspect
|
||||
|
Loading…
Reference in New Issue
Block a user