Add localboot support for uefi boot mode
This commit adds localboot support for uefi boot mode. For uefi localboot, we switch partition table to gpt and create a efi system partition. The efi system partition is used later by bootloader (which is installed from ramdisk). Corresponding diskimage-builder change is here: Idf7ac5987e14e1d31311834196ca7283deec15c6 Implements: blueprint local-boot-support-with-partition-images Change-Id: I00ac31da325676ea4ea1ac4185f5ac3a52c5809a
This commit is contained in:
parent
dc08463ec1
commit
da9ed8d0eb
@ -774,6 +774,10 @@
|
||||
# Options defined in ironic.drivers.modules.deploy_utils
|
||||
#
|
||||
|
||||
# Size of EFI system partition in MiB when configuring UEFI
|
||||
# systems for local boot. (integer value)
|
||||
#efi_system_partition_size=200
|
||||
|
||||
# Block size to use when writing to the nodes disk. (string
|
||||
# value)
|
||||
#dd_block_size=1M
|
||||
|
@ -49,6 +49,10 @@ from ironic.openstack.common import log as logging
|
||||
|
||||
|
||||
deploy_opts = [
|
||||
cfg.IntOpt('efi_system_partition_size',
|
||||
default=200,
|
||||
help='Size of EFI system partition in MiB when configuring '
|
||||
'UEFI systems for local boot.'),
|
||||
cfg.StrOpt('dd_block_size',
|
||||
default='1M',
|
||||
help='Block size to use when writing to the nodes disk.'),
|
||||
@ -209,7 +213,8 @@ def get_disk_identifier(dev):
|
||||
|
||||
|
||||
def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
configdrive_mb, commit=True, boot_option="netboot"):
|
||||
configdrive_mb, commit=True, boot_option="netboot",
|
||||
boot_mode="bios"):
|
||||
"""Partition the disk device.
|
||||
|
||||
Create partitions for root, swap, ephemeral and configdrive on a
|
||||
@ -225,6 +230,7 @@ def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
:param commit: True/False. Default for this setting is True. If False
|
||||
partitions will not be written to disk.
|
||||
:param boot_option: Can be "local" or "netboot". "netboot" by default.
|
||||
:param boot_mode: Can be "bios" or "uefi". "bios" by default.
|
||||
:returns: A dictionary containing the partition type as Key and partition
|
||||
path as Value for the partitions created by this method.
|
||||
|
||||
@ -233,7 +239,18 @@ def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
{'dev': dev})
|
||||
part_template = dev + '-part%d'
|
||||
part_dict = {}
|
||||
|
||||
# For uefi localboot, switch partition table to gpt and create the efi
|
||||
# system partition as the first partition.
|
||||
if boot_mode == "uefi" and boot_option == "local":
|
||||
dp = disk_partitioner.DiskPartitioner(dev, disk_label="gpt")
|
||||
part_num = dp.add_partition(CONF.deploy.efi_system_partition_size,
|
||||
fs_type='fat32',
|
||||
bootable=True)
|
||||
part_dict['efi system partition'] = part_template % part_num
|
||||
else:
|
||||
dp = disk_partitioner.DiskPartitioner(dev)
|
||||
|
||||
if ephemeral_mb:
|
||||
LOG.debug("Add ephemeral partition (%(size)d MB) to device: %(dev)s",
|
||||
{'dev': dev, 'size': ephemeral_mb})
|
||||
@ -255,7 +272,8 @@ def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
# partition until the end of the disk.
|
||||
LOG.debug("Add root partition (%(size)d MB) to device: %(dev)s",
|
||||
{'dev': dev, 'size': root_mb})
|
||||
part_num = dp.add_partition(root_mb, bootable=(boot_option == "local"))
|
||||
part_num = dp.add_partition(root_mb, bootable=(boot_option == "local" and
|
||||
boot_mode == "bios"))
|
||||
part_dict['root'] = part_template % part_num
|
||||
|
||||
if commit:
|
||||
@ -296,13 +314,11 @@ def populate_image(src, dst):
|
||||
images.convert_image(src, dst, 'raw', True)
|
||||
|
||||
|
||||
def mkswap(dev, label='swap1'):
|
||||
"""Execute mkswap on a device."""
|
||||
utils.mkfs('swap', dev, label)
|
||||
|
||||
|
||||
def mkfs_ephemeral(dev, ephemeral_format, label="ephemeral0"):
|
||||
utils.mkfs(ephemeral_format, dev, label)
|
||||
# TODO(rameshg87): Remove this one-line method and use utils.mkfs
|
||||
# directly.
|
||||
def mkfs(fs, dev, label=None):
|
||||
"""Execute mkfs on a device."""
|
||||
utils.mkfs(fs, dev, label)
|
||||
|
||||
|
||||
def block_uuid(dev):
|
||||
@ -518,7 +534,8 @@ def _get_configdrive(configdrive, node_uuid):
|
||||
|
||||
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
image_path, node_uuid, preserve_ephemeral=False,
|
||||
configdrive=None, boot_option="netboot"):
|
||||
configdrive=None, boot_option="netboot",
|
||||
boot_mode="bios"):
|
||||
"""Create partitions and copy an image to the root partition.
|
||||
|
||||
:param dev: Path for the device to work on.
|
||||
@ -536,6 +553,7 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
: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_mode: Can be "bios" or "uefi". "bios" by default.
|
||||
:returns: the UUID of the root partition.
|
||||
"""
|
||||
# the only way for preserve_ephemeral to be set to true is if we are
|
||||
@ -556,7 +574,8 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
|
||||
part_dict = make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
configdrive_mb, commit=commit,
|
||||
boot_option=boot_option)
|
||||
boot_option=boot_option,
|
||||
boot_mode=boot_mode)
|
||||
|
||||
ephemeral_part = part_dict.get('ephemeral')
|
||||
swap_part = part_dict.get('swap')
|
||||
@ -567,7 +586,8 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
raise exception.InstanceDeployFailure(
|
||||
_("Root device '%s' not found") % root_part)
|
||||
|
||||
for part in ('swap', 'ephemeral', 'configdrive'):
|
||||
for part in ('swap', 'ephemeral', 'configdrive',
|
||||
'efi system partition'):
|
||||
part_device = part_dict.get(part)
|
||||
LOG.debug("Checking for %(part)s device (%(dev)s) on node "
|
||||
"%(node)s.", {'part': part, 'dev': part_device,
|
||||
@ -577,6 +597,12 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
_("'%(partition)s' device '%(part_device)s' not found") %
|
||||
{'partition': part, 'part_device': part_device})
|
||||
|
||||
# If it's a uefi localboot, then we have created the efi system
|
||||
# partition. Create a fat filesystem on it.
|
||||
if boot_mode == "uefi" and boot_option == "local":
|
||||
efi_system_part = part_dict.get('efi system partition')
|
||||
mkfs(dev=efi_system_part, fs='vfat', label='efi-part')
|
||||
|
||||
if configdrive_part:
|
||||
# Copy the configdrive content to the configdrive partition
|
||||
dd(configdrive_file, configdrive_part)
|
||||
@ -590,10 +616,10 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
populate_image(image_path, root_part)
|
||||
|
||||
if swap_part:
|
||||
mkswap(swap_part)
|
||||
mkfs(dev=swap_part, fs='swap', label='swap1')
|
||||
|
||||
if ephemeral_part and not preserve_ephemeral:
|
||||
mkfs_ephemeral(ephemeral_part, ephemeral_format)
|
||||
mkfs(dev=ephemeral_part, fs=ephemeral_format, label="ephemeral0")
|
||||
|
||||
try:
|
||||
root_uuid = block_uuid(root_part)
|
||||
@ -607,7 +633,7 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
def deploy_partition_image(address, port, iqn, lun, image_path,
|
||||
root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid,
|
||||
preserve_ephemeral=False, configdrive=None,
|
||||
boot_option="netboot"):
|
||||
boot_option="netboot", boot_mode="bios"):
|
||||
"""All-in-one function to deploy a partition image to a node.
|
||||
|
||||
:param address: The iSCSI IP address.
|
||||
@ -628,6 +654,7 @@ def deploy_partition_image(address, port, iqn, lun, image_path,
|
||||
: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_mode: Can be "bios" or "uefi". "bios" by default.
|
||||
:returns: the UUID of the root partition.
|
||||
"""
|
||||
with _iscsi_setup_and_handle_errors(address, port, iqn,
|
||||
@ -640,7 +667,8 @@ def deploy_partition_image(address, port, iqn, lun, image_path,
|
||||
ephemeral_format, image_path, node_uuid,
|
||||
preserve_ephemeral=preserve_ephemeral,
|
||||
configdrive=configdrive,
|
||||
boot_option=boot_option)
|
||||
boot_option=boot_option,
|
||||
boot_mode=boot_mode)
|
||||
|
||||
return root_uuid
|
||||
|
||||
|
@ -3,7 +3,7 @@ default=deploy
|
||||
image={{pxe_options.deployment_aki_path}}
|
||||
label=deploy
|
||||
initrd={{pxe_options.deployment_ari_path}}
|
||||
append="selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} ip=%I:{{pxe_options.tftp_server}}:%G:%M:%H::on" {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }}
|
||||
append="selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} ip=%I:{{pxe_options.tftp_server}}:%G:%M:%H::on {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }} boot_option={{ pxe_options.boot_option }} boot_mode={{ pxe_options['boot_mode'] }}"
|
||||
|
||||
|
||||
image={{pxe_options.aki_path}}
|
||||
|
@ -177,7 +177,8 @@ def _clean_up_boot_iso_for_instance(node):
|
||||
|
||||
:param node: an ironic node object.
|
||||
"""
|
||||
if not node.instance_info['ilo_boot_iso'].startswith('swift'):
|
||||
ilo_boot_iso = node.instance_info.get('ilo_boot_iso')
|
||||
if not (ilo_boot_iso and ilo_boot_iso.startswith('swift')):
|
||||
return
|
||||
swift_api = swift.SwiftAPI()
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
|
@ -233,7 +233,8 @@ def get_deploy_info(node, **kwargs):
|
||||
if i_info['deploy_key'] != deploy_key:
|
||||
raise exception.InvalidParameterValue(_("Deploy key does not match"))
|
||||
|
||||
params = {'address': kwargs.get('address'),
|
||||
params = {
|
||||
'address': kwargs.get('address'),
|
||||
'port': kwargs.get('port', '3260'),
|
||||
'iqn': kwargs.get('iqn'),
|
||||
'lun': kwargs.get('lun', '1'),
|
||||
@ -246,7 +247,8 @@ def get_deploy_info(node, **kwargs):
|
||||
'swap_mb': int(i_info['swap_mb']),
|
||||
'ephemeral_mb': 1024 * int(i_info['ephemeral_gb']),
|
||||
'preserve_ephemeral': i_info['preserve_ephemeral'],
|
||||
'boot_option': get_boot_option(node)})
|
||||
'boot_option': get_boot_option(node),
|
||||
'boot_mode': _get_boot_mode(node)})
|
||||
|
||||
missing = [key for key in params if params[key] is None]
|
||||
if missing:
|
||||
@ -407,17 +409,30 @@ def parse_root_device_hints(node):
|
||||
|
||||
|
||||
def get_boot_option(node):
|
||||
"""Get the boot mode.
|
||||
"""Gets the boot option.
|
||||
|
||||
:param node: A single Node.
|
||||
:raises: InvalidParameterValue if the capabilities string is not a
|
||||
dict or is malformed.
|
||||
:returns: A string representing the boot mode type. Defaults to 'netboot'.
|
||||
:returns: A string representing the boot option type. Defaults to
|
||||
'netboot'.
|
||||
"""
|
||||
capabilities = deploy_utils.parse_instance_info_capabilities(node)
|
||||
return capabilities.get('boot_option', 'netboot').lower()
|
||||
|
||||
|
||||
def _get_boot_mode(node):
|
||||
"""Gets the boot mode.
|
||||
|
||||
:param node: A single Node.
|
||||
:returns: A string representing the boot mode type. Defaults to 'bios'.
|
||||
"""
|
||||
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
|
||||
if boot_mode:
|
||||
return boot_mode.lower()
|
||||
return "bios"
|
||||
|
||||
|
||||
def build_deploy_ramdisk_options(node):
|
||||
"""Build the ramdisk config options for a node
|
||||
|
||||
@ -446,6 +461,7 @@ def build_deploy_ramdisk_options(node):
|
||||
'ironic_api_url': ironic_api,
|
||||
'disk': CONF.pxe.disk_devices,
|
||||
'boot_option': get_boot_option(node),
|
||||
'boot_mode': _get_boot_mode(node),
|
||||
}
|
||||
|
||||
root_device = parse_root_device_hints(node)
|
||||
|
@ -328,17 +328,6 @@ class PXEDeploy(base.DeployInterface):
|
||||
driver_utils.validate_boot_option_capability(node)
|
||||
|
||||
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
|
||||
boot_option = driver_utils.get_node_capability(node, 'boot_option')
|
||||
|
||||
# NOTE(lucasagomes): We don't support UEFI + localboot because
|
||||
# we do not support creating an EFI boot partition, including the
|
||||
# EFI modules and managing the bootloader variables via efibootmgr.
|
||||
if boot_mode == 'uefi' and boot_option == 'local':
|
||||
error_msg = (_("Local boot is requested, but can't be "
|
||||
"used with node %s because it's configured "
|
||||
"to use UEFI boot") % node.uuid)
|
||||
LOG.error(error_msg)
|
||||
raise exception.InvalidParameterValue(error_msg)
|
||||
|
||||
if CONF.pxe.ipxe_enabled:
|
||||
if not CONF.pxe.http_url or not CONF.pxe.http_root:
|
||||
|
@ -2,7 +2,7 @@ default deploy
|
||||
|
||||
label deploy
|
||||
kernel {{ pxe_options.deployment_aki_path }}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} boot_option={{ pxe_options.boot_option }} {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} boot_option={{ pxe_options.boot_option }} {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }} boot_mode={{ pxe_options['boot_mode'] }}
|
||||
ipappend 3
|
||||
|
||||
|
||||
|
@ -199,6 +199,12 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
swift_obj_mock.delete_object.assert_called_once_with('ilo-cont',
|
||||
'boot-object')
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_get_boot_iso_object_name')
|
||||
def test__clean_up_boot_iso_for_instance_no_boot_iso(
|
||||
self, boot_object_name_mock):
|
||||
ilo_deploy._clean_up_boot_iso_for_instance(self.node)
|
||||
self.assertFalse(boot_object_name_mock.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'check_for_missing_params')
|
||||
def test__parse_driver_info(self, check_params_mock):
|
||||
self.node.driver_info['ilo_deploy_iso'] = 'deploy-iso-uuid'
|
||||
|
@ -2,7 +2,7 @@ default deploy
|
||||
|
||||
label deploy
|
||||
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_kernel
|
||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk selinux=0 disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 troubleshoot=0 text test_param boot_option=netboot root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh
|
||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk selinux=0 disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 troubleshoot=0 text test_param boot_option=netboot root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh boot_mode=bios
|
||||
ipappend 3
|
||||
|
||||
|
||||
|
@ -233,7 +233,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.attach_mock(mocker, name)
|
||||
return parent_mock
|
||||
|
||||
def _test_deploy_partition_image(self, boot_option=None):
|
||||
def _test_deploy_partition_image(self, boot_option=None, boot_mode=None):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -254,7 +254,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
|
||||
name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
|
||||
'logout_iscsi', 'delete_iscsi', 'make_partitions',
|
||||
'is_block_device', 'populate_image', 'mkswap',
|
||||
'is_block_device', 'populate_image', 'mkfs',
|
||||
'block_uuid', 'notify', 'destroy_disk_metadata']
|
||||
parent_mock = self._mock_calls(name_list)
|
||||
parent_mock.get_dev.return_value = dev
|
||||
@ -263,41 +263,46 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.block_uuid.return_value = root_uuid
|
||||
parent_mock.make_partitions.return_value = {'root': root_part,
|
||||
'swap': swap_part}
|
||||
|
||||
make_partitions_expected_args = [dev, root_mb, swap_mb, ephemeral_mb,
|
||||
configdrive_mb]
|
||||
make_partitions_expected_kwargs = {'commit': True}
|
||||
deploy_kwargs = {}
|
||||
|
||||
if boot_option:
|
||||
make_partitions_expected_kwargs['boot_option'] = boot_option
|
||||
deploy_kwargs['boot_option'] = boot_option
|
||||
else:
|
||||
make_partitions_expected_kwargs['boot_option'] = 'netboot'
|
||||
|
||||
if boot_mode:
|
||||
make_partitions_expected_kwargs['boot_mode'] = boot_mode
|
||||
deploy_kwargs['boot_mode'] = boot_mode
|
||||
else:
|
||||
make_partitions_expected_kwargs['boot_mode'] = 'bios'
|
||||
|
||||
# If no boot_option, then it should default to netboot.
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.discovery(address, port),
|
||||
mock.call.login_iscsi(address, port, iqn),
|
||||
mock.call.is_block_device(dev),
|
||||
mock.call.get_image_mb(image_path),
|
||||
mock.call.destroy_disk_metadata(dev, node_uuid)]
|
||||
|
||||
if boot_option:
|
||||
calls_expected.append(mock.call.make_partitions(
|
||||
dev, root_mb, swap_mb, ephemeral_mb, configdrive_mb,
|
||||
commit=True, boot_option=boot_option))
|
||||
else:
|
||||
# If no boot_option, then it should default to netboot.
|
||||
calls_expected.append(mock.call.make_partitions(
|
||||
dev, root_mb, swap_mb, ephemeral_mb, configdrive_mb,
|
||||
commit=True, boot_option="netboot"))
|
||||
|
||||
calls_expected.extend([mock.call.is_block_device(root_part),
|
||||
mock.call.destroy_disk_metadata(dev, node_uuid),
|
||||
mock.call.make_partitions(
|
||||
*make_partitions_expected_args,
|
||||
**make_partitions_expected_kwargs),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.is_block_device(swap_part),
|
||||
mock.call.populate_image(image_path, root_part),
|
||||
mock.call.mkswap(swap_part),
|
||||
mock.call.mkfs(dev=swap_part, fs='swap',
|
||||
label='swap1'),
|
||||
mock.call.block_uuid(root_part),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)])
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
kwargs = {}
|
||||
if boot_option:
|
||||
kwargs = {'boot_option': boot_option}
|
||||
|
||||
returned_root_uuid = utils.deploy_partition_image(address, port, iqn,
|
||||
lun, image_path,
|
||||
root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format,
|
||||
node_uuid, **kwargs)
|
||||
returned_root_uuid = utils.deploy_partition_image(
|
||||
address, port, iqn, lun, image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format, node_uuid, **deploy_kwargs)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
@ -311,6 +316,87 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
def test_deploy_partition_image_localboot(self):
|
||||
self._test_deploy_partition_image(boot_option="local")
|
||||
|
||||
def test_deploy_partition_image_wo_boot_option_and_wo_boot_mode(self):
|
||||
self._test_deploy_partition_image()
|
||||
|
||||
def test_deploy_partition_image_netboot_bios(self):
|
||||
self._test_deploy_partition_image(boot_option="netboot",
|
||||
boot_mode="bios")
|
||||
|
||||
def test_deploy_partition_image_localboot_bios(self):
|
||||
self._test_deploy_partition_image(boot_option="local",
|
||||
boot_mode="bios")
|
||||
|
||||
def test_deploy_partition_image_netboot_uefi(self):
|
||||
self._test_deploy_partition_image(boot_option="netboot",
|
||||
boot_mode="uefi")
|
||||
|
||||
def test_deploy_partition_image_localboot_uefi(self):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
iqn = 'iqn.xyz'
|
||||
lun = 1
|
||||
image_path = '/tmp/xyz/image'
|
||||
root_mb = 128
|
||||
swap_mb = 64
|
||||
ephemeral_mb = 0
|
||||
ephemeral_format = None
|
||||
configdrive_mb = 0
|
||||
node_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
|
||||
|
||||
dev = '/dev/fake'
|
||||
swap_part = '/dev/fake-part2'
|
||||
root_part = '/dev/fake-part3'
|
||||
efi_system_part = '/dev/fake-part1'
|
||||
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
|
||||
|
||||
name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
|
||||
'logout_iscsi', 'delete_iscsi', 'make_partitions',
|
||||
'is_block_device', 'populate_image', 'mkfs',
|
||||
'block_uuid', 'notify', 'destroy_disk_metadata']
|
||||
parent_mock = self._mock_calls(name_list)
|
||||
parent_mock.get_dev.return_value = dev
|
||||
parent_mock.get_image_mb.return_value = 1
|
||||
parent_mock.is_block_device.return_value = True
|
||||
parent_mock.block_uuid.return_value = root_uuid
|
||||
parent_mock.make_partitions.return_value = {
|
||||
'root': root_part, 'swap': swap_part,
|
||||
'efi system partition': efi_system_part}
|
||||
|
||||
# If no boot_option, then it should default to netboot.
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.discovery(address, port),
|
||||
mock.call.login_iscsi(address, port, iqn),
|
||||
mock.call.is_block_device(dev),
|
||||
mock.call.get_image_mb(image_path),
|
||||
mock.call.destroy_disk_metadata(dev, node_uuid),
|
||||
mock.call.make_partitions(dev, root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
commit=True,
|
||||
boot_option="local",
|
||||
boot_mode="uefi"),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.is_block_device(swap_part),
|
||||
mock.call.is_block_device(efi_system_part),
|
||||
mock.call.mkfs(dev=efi_system_part, fs='vfat',
|
||||
label='efi-part'),
|
||||
mock.call.populate_image(image_path, root_part),
|
||||
mock.call.mkfs(dev=swap_part, fs='swap',
|
||||
label='swap1'),
|
||||
mock.call.block_uuid(root_part),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
returned_root_uuid = utils.deploy_partition_image(
|
||||
address, port, iqn, lun, image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format, node_uuid, boot_option="local",
|
||||
boot_mode="uefi")
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
|
||||
def test_deploy_partition_image_without_swap(self):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
@ -349,7 +435,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
commit=True,
|
||||
boot_option="netboot"),
|
||||
boot_option="netboot",
|
||||
boot_mode="bios"),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.populate_image(image_path, root_part),
|
||||
mock.call.block_uuid(root_part),
|
||||
@ -388,9 +475,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
|
||||
name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
|
||||
'logout_iscsi', 'delete_iscsi', 'make_partitions',
|
||||
'is_block_device', 'populate_image', 'mkswap',
|
||||
'block_uuid', 'notify', 'mkfs_ephemeral',
|
||||
'destroy_disk_metadata']
|
||||
'is_block_device', 'populate_image', 'mkfs',
|
||||
'block_uuid', 'notify', 'destroy_disk_metadata']
|
||||
parent_mock = self._mock_calls(name_list)
|
||||
parent_mock.get_dev.return_value = dev
|
||||
parent_mock.get_image_mb.return_value = 1
|
||||
@ -409,14 +495,17 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
commit=True,
|
||||
boot_option="netboot"),
|
||||
boot_option="netboot",
|
||||
boot_mode="bios"),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.is_block_device(swap_part),
|
||||
mock.call.is_block_device(ephemeral_part),
|
||||
mock.call.populate_image(image_path, root_part),
|
||||
mock.call.mkswap(swap_part),
|
||||
mock.call.mkfs_ephemeral(ephemeral_part,
|
||||
ephemeral_format),
|
||||
mock.call.mkfs(dev=swap_part, fs='swap',
|
||||
label='swap1'),
|
||||
mock.call.mkfs(dev=ephemeral_part,
|
||||
fs=ephemeral_format,
|
||||
label='ephemeral0'),
|
||||
mock.call.block_uuid(root_part),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
@ -453,9 +542,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
|
||||
name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
|
||||
'logout_iscsi', 'delete_iscsi', 'make_partitions',
|
||||
'is_block_device', 'populate_image', 'mkswap',
|
||||
'block_uuid', 'notify', 'mkfs_ephemeral',
|
||||
'get_dev_block_size']
|
||||
'is_block_device', 'populate_image', 'mkfs',
|
||||
'block_uuid', 'notify', 'get_dev_block_size']
|
||||
parent_mock = self._mock_calls(name_list)
|
||||
parent_mock.get_dev.return_value = dev
|
||||
parent_mock.get_image_mb.return_value = 1
|
||||
@ -474,12 +562,14 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
commit=False,
|
||||
boot_option="netboot"),
|
||||
boot_option="netboot",
|
||||
boot_mode="bios"),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.is_block_device(swap_part),
|
||||
mock.call.is_block_device(ephemeral_part),
|
||||
mock.call.populate_image(image_path, root_part),
|
||||
mock.call.mkswap(swap_part),
|
||||
mock.call.mkfs(dev=swap_part, fs='swap',
|
||||
label='swap1'),
|
||||
mock.call.block_uuid(root_part),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
@ -493,7 +583,6 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
preserve_ephemeral=True,
|
||||
boot_option="netboot")
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertFalse(parent_mock.mkfs_ephemeral.called)
|
||||
self.assertFalse(parent_mock.get_dev_block_size.called)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
|
||||
@ -544,7 +633,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
commit=True,
|
||||
boot_option="netboot"),
|
||||
boot_option="netboot",
|
||||
boot_mode="bios"),
|
||||
mock.call.is_block_device(root_part),
|
||||
mock.call.is_block_device(configdrive_part),
|
||||
mock.call.dd(mock.ANY, configdrive_part),
|
||||
@ -726,7 +816,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
ephemeral_format, image_path,
|
||||
node_uuid, configdrive=None,
|
||||
preserve_ephemeral=False,
|
||||
boot_option="netboot"),
|
||||
boot_option="netboot",
|
||||
boot_mode="bios"),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
@ -902,7 +993,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
|
||||
self.swap_mb, self.ephemeral_mb,
|
||||
self.configdrive_mb, commit=True,
|
||||
boot_option="netboot")
|
||||
boot_option="netboot",
|
||||
boot_mode="bios")
|
||||
|
||||
def test_no_swap_partition(self):
|
||||
self.mock_ibd.side_effect = [True, False]
|
||||
@ -916,7 +1008,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
|
||||
self.swap_mb, self.ephemeral_mb,
|
||||
self.configdrive_mb, commit=True,
|
||||
boot_option="netboot")
|
||||
boot_option="netboot",
|
||||
boot_mode="bios")
|
||||
|
||||
def test_no_ephemeral_partition(self):
|
||||
ephemeral_part = '/dev/fake-part1'
|
||||
@ -940,7 +1033,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
|
||||
self.swap_mb, ephemeral_mb,
|
||||
self.configdrive_mb, commit=True,
|
||||
boot_option="netboot")
|
||||
boot_option="netboot",
|
||||
boot_mode="bios")
|
||||
|
||||
@mock.patch.object(common_utils, 'unlink_without_raise')
|
||||
@mock.patch.object(utils, '_get_configdrive')
|
||||
@ -970,7 +1064,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
|
||||
self.swap_mb, self.ephemeral_mb,
|
||||
configdrive_mb, commit=True,
|
||||
boot_option="netboot")
|
||||
boot_option="netboot",
|
||||
boot_mode="bios")
|
||||
mock_unlink.assert_called_once_with('fake-path')
|
||||
|
||||
|
||||
|
@ -412,7 +412,8 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
|
||||
def _test_build_deploy_ramdisk_options(self, mock_alnum, api_url,
|
||||
expected_root_device=None,
|
||||
expected_boot_option='netboot'):
|
||||
expected_boot_option='netboot',
|
||||
expected_boot_mode='bios'):
|
||||
fake_key = '0123456789ABCDEFGHIJKLMNOPQRSTUV'
|
||||
fake_disk = 'fake-disk'
|
||||
|
||||
@ -420,12 +421,15 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
|
||||
mock_alnum.return_value = fake_key
|
||||
|
||||
expected_opts = {'iscsi_target_iqn': 'iqn-%s' % self.node.uuid,
|
||||
expected_opts = {
|
||||
'iscsi_target_iqn': 'iqn-%s' % self.node.uuid,
|
||||
'deployment_id': self.node.uuid,
|
||||
'deployment_key': fake_key,
|
||||
'disk': fake_disk,
|
||||
'ironic_api_url': api_url,
|
||||
'boot_option': expected_boot_option}
|
||||
'boot_option': expected_boot_option,
|
||||
'boot_mode': expected_boot_mode,
|
||||
}
|
||||
|
||||
if expected_root_device:
|
||||
expected_opts['root_device'] = expected_root_device
|
||||
@ -482,6 +486,16 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
self._test_build_deploy_ramdisk_options(mock_alnum, fake_api_url,
|
||||
expected_boot_option=expected)
|
||||
|
||||
@mock.patch.object(keystone, 'get_service_url')
|
||||
@mock.patch.object(utils, 'random_alnum')
|
||||
def test_build_deploy_ramdisk_options_boot_mode(self, mock_alnum,
|
||||
mock_get_url):
|
||||
self.node.properties['capabilities'] = 'boot_mode:uefi'
|
||||
fake_api_url = 'http://127.0.0.1:6385'
|
||||
self.config(api_url=fake_api_url, group='conductor')
|
||||
self._test_build_deploy_ramdisk_options(mock_alnum, fake_api_url,
|
||||
expected_boot_mode='uefi')
|
||||
|
||||
def test_parse_root_device_hints(self):
|
||||
self.node.properties['root_device'] = {'wwn': 123456}
|
||||
expected = 'wwn=123456'
|
||||
|
@ -193,12 +193,15 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
self.config(api_url='http://192.168.122.184:6385', group='conductor')
|
||||
self.config(disk_devices='sda', group='pxe')
|
||||
|
||||
fake_deploy_opts = {'iscsi_target_iqn': 'fake-iqn',
|
||||
fake_deploy_opts = {
|
||||
'iscsi_target_iqn': 'fake-iqn',
|
||||
'deployment_id': 'fake-deploy-id',
|
||||
'deployment_key': 'fake-deploy-key',
|
||||
'disk': 'fake-disk',
|
||||
'ironic_api_url': 'fake-api-url',
|
||||
'boot_option': 'netboot'}
|
||||
'boot_option': 'netboot',
|
||||
'boot_mode': 'bios',
|
||||
}
|
||||
|
||||
deploy_opts_mock.return_value = fake_deploy_opts
|
||||
|
||||
@ -237,6 +240,7 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
'boot_option': 'netboot',
|
||||
'ipa-api-url': CONF.conductor.api_url,
|
||||
'ipa-driver-name': self.node.driver,
|
||||
'boot_mode': 'bios',
|
||||
}
|
||||
|
||||
expected_options.update(fake_deploy_opts)
|
||||
@ -475,17 +479,6 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.deploy.validate, task)
|
||||
|
||||
@mock.patch.object(base_image_service.BaseImageService, '_show')
|
||||
def test_validate_fail_invalid_uefi_and_localboot(self, mock_glance):
|
||||
properties = {'capabilities': 'boot_mode:uefi,boot_option:local'}
|
||||
mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
|
||||
'ramdisk_id': 'fake-initr'}}
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.properties = properties
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.deploy.validate, task)
|
||||
|
||||
def test_validate_fail_no_port(self):
|
||||
new_node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
|
@ -53,6 +53,7 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
'boot_option': 'netboot',
|
||||
'ipa-api-url': 'http://192.168.122.184:6385',
|
||||
'ipa-driver-name': 'pxe_ssh',
|
||||
'boot_mode': 'bios',
|
||||
}
|
||||
self.agent_pxe_options = {
|
||||
'deployment_ari_path': u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7'
|
||||
|
Loading…
x
Reference in New Issue
Block a user