Add support for partition images in agent drivers
This patch enables the partition image support for agent drivers which support `boot` iterface. Partial-Bug: 1526289 Co-Authored-By: Nisha Agarwal <agarwalnisha1980@gmail.com> Depends-on: I22bc29a39bf5c35f3eecb6d4e51cebd6aee0ce1 Depends-on: I37908470484744bb720f741d378106d1cb1227a3 Change-Id: Ifc8ba098f13b6fde712a584798fceb0321137bc9
This commit is contained in:
parent
6d1b7c6d0d
commit
644a1c36b4
@ -111,6 +111,11 @@ OPTIONAL_PROPERTIES = {
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||
|
||||
PARTITION_IMAGE_LABELS = ('kernel', 'ramdisk', 'root_gb', 'root_mb', 'swap_mb',
|
||||
'ephemeral_mb', 'ephemeral_format', 'configdrive',
|
||||
'preserve_ephemeral', 'image_type',
|
||||
'deploy_boot_mode')
|
||||
|
||||
|
||||
def build_instance_info_for_deploy(task):
|
||||
"""Build instance_info necessary for deploying to a node.
|
||||
@ -123,7 +128,7 @@ def build_instance_info_for_deploy(task):
|
||||
"""
|
||||
node = task.node
|
||||
instance_info = node.instance_info
|
||||
|
||||
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||
image_source = instance_info['image_source']
|
||||
if service_utils.is_glance_image(image_source):
|
||||
glance = image_service.GlanceImageService(version=2,
|
||||
@ -137,6 +142,10 @@ def build_instance_info_for_deploy(task):
|
||||
instance_info['image_disk_format'] = image_info['disk_format']
|
||||
instance_info['image_container_format'] = (
|
||||
image_info['container_format'])
|
||||
|
||||
if not iwdi:
|
||||
instance_info['kernel'] = image_info['properties']['kernel_id']
|
||||
instance_info['ramdisk'] = image_info['properties']['ramdisk_id']
|
||||
else:
|
||||
try:
|
||||
image_service.HttpImageService().validate_href(image_source)
|
||||
@ -148,6 +157,12 @@ def build_instance_info_for_deploy(task):
|
||||
"is not reachable."), image_source)
|
||||
instance_info['image_url'] = image_source
|
||||
|
||||
if not iwdi:
|
||||
instance_info['image_type'] = 'partition'
|
||||
i_info = deploy_utils.parse_instance_info(node)
|
||||
instance_info.update(i_info)
|
||||
else:
|
||||
instance_info['image_type'] = 'whole-disk-image'
|
||||
return instance_info
|
||||
|
||||
|
||||
@ -256,6 +271,7 @@ class AgentDeploy(base.DeployInterface):
|
||||
params['instance_info.image_source'] = image_source
|
||||
error_msg = _('Node %s failed to validate deploy image info. Some '
|
||||
'parameters were missing') % node.uuid
|
||||
|
||||
deploy_utils.check_for_missing_params(params, error_msg)
|
||||
|
||||
if not service_utils.is_glance_image(image_source):
|
||||
@ -265,15 +281,6 @@ class AgentDeploy(base.DeployInterface):
|
||||
"instance_info for node %s") % node.uuid)
|
||||
|
||||
check_image_size(task, image_source)
|
||||
is_whole_disk_image = node.driver_internal_info.get(
|
||||
'is_whole_disk_image')
|
||||
# TODO(sirushtim): Remove once IPA has support for partition images.
|
||||
if is_whole_disk_image is False:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"Node %(node)s is configured to use the %(driver)s driver "
|
||||
"which currently does not support deploying partition "
|
||||
"images.") % {'node': node.uuid, 'driver': node.driver})
|
||||
|
||||
# Validate the root device hints
|
||||
deploy_utils.parse_root_device_hints(node)
|
||||
|
||||
@ -468,11 +475,44 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
if no_proxy is not None:
|
||||
image_info['no_proxy'] = no_proxy
|
||||
|
||||
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||
if not iwdi:
|
||||
for label in PARTITION_IMAGE_LABELS:
|
||||
image_info[label] = node.instance_info.get(label)
|
||||
boot_option = deploy_utils.get_boot_option(node)
|
||||
boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
|
||||
if boot_mode:
|
||||
image_info['deploy_boot_mode'] = boot_mode
|
||||
else:
|
||||
image_info['deploy_boot_mode'] = 'bios'
|
||||
image_info['boot_option'] = boot_option
|
||||
|
||||
# Tell the client to download and write the image with the given args
|
||||
self._client.prepare_image(node, image_info)
|
||||
|
||||
task.process_event('wait')
|
||||
|
||||
def _get_uuid_from_result(self, task, type_uuid):
|
||||
command = self._client.get_commands_status(task.node)[-1]
|
||||
|
||||
if command['command_result'] is not None:
|
||||
words = command['command_result']['result'].split()
|
||||
for word in words:
|
||||
if type_uuid in word:
|
||||
result = word.split('=')[1]
|
||||
if not result:
|
||||
msg = (_('Command result did not return %(type_uuid)s '
|
||||
'for node %(node)s. The version of the IPA '
|
||||
'ramdisk used in the deployment might not '
|
||||
'have support for provisioning of '
|
||||
'partition images.') %
|
||||
{'type_uuid': type_uuid,
|
||||
'node': task.node.uuid})
|
||||
LOG.error(msg)
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
return
|
||||
return result
|
||||
|
||||
def check_deploy_success(self, node):
|
||||
# should only ever be called after we've validated that
|
||||
# the prepare_image command is complete
|
||||
@ -483,6 +523,7 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
def reboot_to_instance(self, task, **kwargs):
|
||||
task.process_event('resume')
|
||||
node = task.node
|
||||
iwdi = task.node.driver_internal_info.get('is_whole_disk_image')
|
||||
error = self.check_deploy_success(node)
|
||||
if error is not None:
|
||||
# TODO(jimrollenhagen) power off if using neutron dhcp to
|
||||
@ -492,11 +533,22 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
LOG.error(msg)
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
return
|
||||
|
||||
if not iwdi:
|
||||
root_uuid = self._get_uuid_from_result(task, 'root_uuid')
|
||||
if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi':
|
||||
efi_sys_uuid = (
|
||||
self._get_uuid_from_result(task,
|
||||
'efi_system_partition_uuid'))
|
||||
else:
|
||||
efi_sys_uuid = None
|
||||
task.node.driver_internal_info['root_uuid_or_disk_id'] = root_uuid
|
||||
task.node.save()
|
||||
self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid)
|
||||
LOG.info(_LI('Image successfully written to node %s'), node.uuid)
|
||||
LOG.debug('Rebooting node %s to instance', node.uuid)
|
||||
if iwdi:
|
||||
manager_utils.node_set_boot_device(task, 'disk', persistent=True)
|
||||
|
||||
manager_utils.node_set_boot_device(task, 'disk', persistent=True)
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
||||
# NOTE(TheJulia): If we deployed a whole disk image, we
|
||||
@ -505,7 +557,7 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
# TODO(rameshg87): Not all in-tree drivers using reboot_to_instance
|
||||
# have a boot interface. So include a check for now. Remove this
|
||||
# check once all in-tree drivers have a boot interface.
|
||||
if task.driver.boot:
|
||||
if task.driver.boot and iwdi:
|
||||
task.driver.boot.clean_up_ramdisk(task)
|
||||
|
||||
|
||||
|
@ -710,6 +710,30 @@ class BaseAgentVendor(base.VendorInterface):
|
||||
task.process_event('done')
|
||||
LOG.info(_LI('Deployment to node %s done'), task.node.uuid)
|
||||
|
||||
def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid):
|
||||
"""Prepares instance to boot.
|
||||
|
||||
:param task: a TaskManager object containing the node
|
||||
:param root_uuid: the UUID for root partition
|
||||
:param efi_sys_uuid: the UUID for the efi partition
|
||||
:raises: InvalidState if fails to prepare instance
|
||||
"""
|
||||
|
||||
node = task.node
|
||||
if deploy_utils.get_boot_option(node) == "local":
|
||||
# Install the boot loader
|
||||
self.configure_local_boot(
|
||||
task, root_uuid=root_uuid,
|
||||
efi_system_part_uuid=efi_sys_uuid)
|
||||
try:
|
||||
task.driver.boot.prepare_instance(task)
|
||||
except Exception as e:
|
||||
LOG.error(_LE('Deploy failed for instance %(instance)s. '
|
||||
'Error: %(error)s'),
|
||||
{'instance': node.instance_uuid, 'error': e})
|
||||
msg = _('Failed to continue agent deployment.')
|
||||
self._log_and_raise_deployment_error(task, msg)
|
||||
|
||||
def configure_local_boot(self, task, root_uuid=None,
|
||||
efi_system_part_uuid=None):
|
||||
"""Helper method to configure local boot on the node.
|
||||
|
@ -3,3 +3,11 @@ default deploy
|
||||
label deploy
|
||||
kernel {{ pxe_options.deployment_aki_path }}
|
||||
append initrd={{ pxe_options.deployment_ari_path }} text {{ pxe_options.pxe_append_params }} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }}{% if pxe_options.root_device %} root_device={{ pxe_options.root_device }}{% endif %} coreos.configdrive=0
|
||||
|
||||
label boot_partition
|
||||
kernel {{ pxe_options.aki_path }}
|
||||
append initrd={{ pxe_options.ari_path }} root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }}
|
||||
|
||||
label boot_whole_disk
|
||||
COM32 chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
|
@ -26,6 +26,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import strutils
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
@ -96,10 +97,12 @@ SUPPORTED_CAPABILITIES = {
|
||||
'disk_label': ('msdos', 'gpt'),
|
||||
}
|
||||
|
||||
DISK_LAYOUT_PARAMS = ('root_gb', 'swap_mb', 'ephemeral_gb')
|
||||
|
||||
# All functions are called from deploy() directly or indirectly.
|
||||
# They are split for stub-out.
|
||||
|
||||
|
||||
def discovery(portal_address, portal_port):
|
||||
"""Do iSCSI discovery on portal."""
|
||||
utils.execute('iscsiadm',
|
||||
@ -1084,3 +1087,111 @@ def get_image_instance_info(node):
|
||||
check_for_missing_params(info, error_msg)
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def parse_instance_info(node):
|
||||
"""Gets the instance specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' property of the
|
||||
supplied node contains the required information for this driver to
|
||||
deploy images to the node.
|
||||
|
||||
:param node: a single Node.
|
||||
:returns: A dict with the instance_info values.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
|
||||
info = node.instance_info
|
||||
i_info = {}
|
||||
i_info['image_source'] = info.get('image_source')
|
||||
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||
if not iwdi:
|
||||
if (i_info['image_source'] and
|
||||
not service_utils.is_glance_image(
|
||||
i_info['image_source'])):
|
||||
i_info['kernel'] = info.get('kernel')
|
||||
i_info['ramdisk'] = info.get('ramdisk')
|
||||
i_info['root_gb'] = info.get('root_gb')
|
||||
|
||||
error_msg = _("Cannot validate driver deploy. Some parameters were missing"
|
||||
" in node's instance_info")
|
||||
check_for_missing_params(i_info, error_msg)
|
||||
|
||||
# Internal use only
|
||||
i_info['deploy_key'] = info.get('deploy_key')
|
||||
i_info['swap_mb'] = int(info.get('swap_mb', 0))
|
||||
i_info['ephemeral_gb'] = info.get('ephemeral_gb', 0)
|
||||
err_msg_invalid = _("Cannot validate parameter for driver deploy. "
|
||||
"Invalid parameter %(param)s. Reason: %(reason)s")
|
||||
for param in DISK_LAYOUT_PARAMS:
|
||||
try:
|
||||
int(i_info[param])
|
||||
except ValueError:
|
||||
reason = _("%s is not an integer value.") % i_info[param]
|
||||
raise exception.InvalidParameterValue(err_msg_invalid %
|
||||
{'param': param,
|
||||
'reason': reason})
|
||||
|
||||
i_info['root_mb'] = 1024 * int(info.get('root_gb'))
|
||||
|
||||
if iwdi:
|
||||
if int(i_info['swap_mb']) > 0 or int(i_info['ephemeral_gb']) > 0:
|
||||
err_msg_invalid = _("Cannot deploy whole disk image with "
|
||||
"swap or ephemeral size set")
|
||||
raise exception.InvalidParameterValue(err_msg_invalid)
|
||||
i_info['ephemeral_format'] = info.get('ephemeral_format')
|
||||
i_info['configdrive'] = info.get('configdrive')
|
||||
|
||||
if i_info['ephemeral_gb'] and not i_info['ephemeral_format']:
|
||||
i_info['ephemeral_format'] = CONF.pxe.default_ephemeral_format
|
||||
|
||||
preserve_ephemeral = info.get('preserve_ephemeral', False)
|
||||
try:
|
||||
i_info['preserve_ephemeral'] = (
|
||||
strutils.bool_from_string(preserve_ephemeral, strict=True))
|
||||
except ValueError as e:
|
||||
raise exception.InvalidParameterValue(
|
||||
err_msg_invalid % {'param': 'preserve_ephemeral', 'reason': e})
|
||||
|
||||
# NOTE(Zhenguo): If rebuilding with preserve_ephemeral option, check
|
||||
# that the disk layout is unchanged.
|
||||
if i_info['preserve_ephemeral']:
|
||||
_check_disk_layout_unchanged(node, i_info)
|
||||
|
||||
return i_info
|
||||
|
||||
|
||||
def _check_disk_layout_unchanged(node, i_info):
|
||||
"""Check whether disk layout is unchanged.
|
||||
|
||||
If the node has already been deployed to, this checks whether the disk
|
||||
layout for the node is the same as when it had been deployed to.
|
||||
|
||||
:param node: the node of interest
|
||||
:param i_info: instance information (a dictionary) for the node, containing
|
||||
disk layout information
|
||||
:raises: InvalidParameterValue if the disk layout changed
|
||||
"""
|
||||
# If a node has been deployed to, this is the instance information
|
||||
# used for that deployment.
|
||||
driver_internal_info = node.driver_internal_info
|
||||
if 'instance' not in driver_internal_info:
|
||||
return
|
||||
|
||||
error_msg = ''
|
||||
for param in DISK_LAYOUT_PARAMS:
|
||||
param_value = int(driver_internal_info['instance'][param])
|
||||
if param_value != int(i_info[param]):
|
||||
error_msg += (_(' Deployed value of %(param)s was %(param_value)s '
|
||||
'but requested value is %(request_value)s.') %
|
||||
{'param': param, 'param_value': param_value,
|
||||
'request_value': i_info[param]})
|
||||
|
||||
if error_msg:
|
||||
err_msg_invalid = _("The following parameters have different values "
|
||||
"from previous deployment:%(error_msg)s")
|
||||
raise exception.InvalidParameterValue(err_msg_invalid %
|
||||
{'error_msg': error_msg})
|
||||
|
@ -20,12 +20,10 @@ from ironic_lib import utils as ironic_utils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import fileutils
|
||||
from oslo_utils import strutils
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from ironic.common import dhcp_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils as glance_service_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
@ -107,39 +105,6 @@ def _get_image_file_path(node_uuid):
|
||||
return os.path.join(_get_image_dir_path(node_uuid), 'disk')
|
||||
|
||||
|
||||
def _check_disk_layout_unchanged(node, i_info):
|
||||
"""Check whether disk layout is unchanged.
|
||||
|
||||
If the node has already been deployed to, this checks whether the disk
|
||||
layout for the node is the same as when it had been deployed to.
|
||||
|
||||
:param node: the node of interest
|
||||
:param i_info: instance information (a dictionary) for the node, containing
|
||||
disk layout information
|
||||
:raises: InvalidParameterValue if the disk layout changed
|
||||
"""
|
||||
# If a node has been deployed to, this is the instance information
|
||||
# used for that deployment.
|
||||
driver_internal_info = node.driver_internal_info
|
||||
if 'instance' not in driver_internal_info:
|
||||
return
|
||||
|
||||
error_msg = ''
|
||||
for param in DISK_LAYOUT_PARAMS:
|
||||
param_value = int(driver_internal_info['instance'][param])
|
||||
if param_value != int(i_info[param]):
|
||||
error_msg += (_(' Deployed value of %(param)s was %(param_value)s '
|
||||
'but requested value is %(request_value)s.') %
|
||||
{'param': param, 'param_value': param_value,
|
||||
'request_value': i_info[param]})
|
||||
|
||||
if error_msg:
|
||||
err_msg_invalid = _("The following parameters have different values "
|
||||
"from previous deployment:%(error_msg)s")
|
||||
raise exception.InvalidParameterValue(err_msg_invalid %
|
||||
{'error_msg': error_msg})
|
||||
|
||||
|
||||
def _save_disk_layout(node, i_info):
|
||||
"""Saves the disk layout.
|
||||
|
||||
@ -159,81 +124,6 @@ def _save_disk_layout(node, i_info):
|
||||
node.save()
|
||||
|
||||
|
||||
def parse_instance_info(node):
|
||||
"""Gets the instance specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' property of the
|
||||
supplied node contains the required information for this driver to
|
||||
deploy images to the node.
|
||||
|
||||
:param node: a single Node.
|
||||
:returns: A dict with the instance_info values.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
info = node.instance_info
|
||||
i_info = {}
|
||||
i_info['image_source'] = info.get('image_source')
|
||||
is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image')
|
||||
if not is_whole_disk_image:
|
||||
if (i_info['image_source'] and
|
||||
not glance_service_utils.is_glance_image(
|
||||
i_info['image_source'])):
|
||||
i_info['kernel'] = info.get('kernel')
|
||||
i_info['ramdisk'] = info.get('ramdisk')
|
||||
i_info['root_gb'] = info.get('root_gb')
|
||||
|
||||
error_msg = _("Cannot validate iSCSI deploy. Some parameters were missing"
|
||||
" in node's instance_info")
|
||||
deploy_utils.check_for_missing_params(i_info, error_msg)
|
||||
|
||||
# Internal use only
|
||||
i_info['deploy_key'] = info.get('deploy_key')
|
||||
|
||||
i_info['swap_mb'] = info.get('swap_mb', 0)
|
||||
i_info['ephemeral_gb'] = info.get('ephemeral_gb', 0)
|
||||
err_msg_invalid = _("Cannot validate parameter for iSCSI deploy. "
|
||||
"Invalid parameter %(param)s. Reason: %(reason)s")
|
||||
for param in DISK_LAYOUT_PARAMS:
|
||||
try:
|
||||
int(i_info[param])
|
||||
except ValueError:
|
||||
reason = _("%s is not an integer value.") % i_info[param]
|
||||
raise exception.InvalidParameterValue(err_msg_invalid %
|
||||
{'param': param,
|
||||
'reason': reason})
|
||||
|
||||
if is_whole_disk_image:
|
||||
if int(i_info['swap_mb']) > 0 or int(i_info['ephemeral_gb']) > 0:
|
||||
err_msg_invalid = _("Cannot deploy whole disk image with "
|
||||
"swap or ephemeral size set")
|
||||
raise exception.InvalidParameterValue(err_msg_invalid)
|
||||
return i_info
|
||||
|
||||
i_info['ephemeral_format'] = info.get('ephemeral_format')
|
||||
i_info['configdrive'] = info.get('configdrive')
|
||||
|
||||
if i_info['ephemeral_gb'] and not i_info['ephemeral_format']:
|
||||
i_info['ephemeral_format'] = CONF.pxe.default_ephemeral_format
|
||||
|
||||
preserve_ephemeral = info.get('preserve_ephemeral', False)
|
||||
try:
|
||||
i_info['preserve_ephemeral'] = (
|
||||
strutils.bool_from_string(preserve_ephemeral, strict=True))
|
||||
except ValueError as e:
|
||||
raise exception.InvalidParameterValue(
|
||||
err_msg_invalid % {'param': 'preserve_ephemeral', 'reason': e})
|
||||
|
||||
# NOTE(Zhenguo): If rebuilding with preserve_ephemeral option, check
|
||||
# that the disk layout is unchanged.
|
||||
if i_info['preserve_ephemeral']:
|
||||
_check_disk_layout_unchanged(node, i_info)
|
||||
|
||||
return i_info
|
||||
|
||||
|
||||
def check_image_size(task):
|
||||
"""Check if the requested image is larger than the root partition size.
|
||||
|
||||
@ -241,7 +131,7 @@ def check_image_size(task):
|
||||
:raises: InstanceDeployFailure if size of the image is greater than root
|
||||
partition.
|
||||
"""
|
||||
i_info = parse_instance_info(task.node)
|
||||
i_info = deploy_utils.parse_instance_info(task.node)
|
||||
image_path = _get_image_file_path(task.node.uuid)
|
||||
image_mb = disk_utils.get_image_mb(image_path)
|
||||
root_mb = 1024 * int(i_info['root_gb'])
|
||||
@ -263,7 +153,7 @@ def cache_instance_image(ctx, node):
|
||||
:returns: a tuple containing the uuid of the image and the path in
|
||||
the filesystem where image is cached.
|
||||
"""
|
||||
i_info = parse_instance_info(node)
|
||||
i_info = deploy_utils.parse_instance_info(node)
|
||||
fileutils.ensure_tree(_get_image_dir_path(node.uuid))
|
||||
image_path = _get_image_file_path(node.uuid)
|
||||
uuid = i_info['image_source']
|
||||
@ -298,7 +188,7 @@ def get_deploy_info(node, **kwargs):
|
||||
value.
|
||||
"""
|
||||
deploy_key = kwargs.get('key')
|
||||
i_info = parse_instance_info(node)
|
||||
i_info = deploy_utils.parse_instance_info(node)
|
||||
if i_info['deploy_key'] != deploy_key:
|
||||
raise exception.InvalidParameterValue(_("Deploy key does not match"))
|
||||
|
||||
@ -410,7 +300,7 @@ def continue_deploy(task, **kwargs):
|
||||
if params.get('preserve_ephemeral', False):
|
||||
# Save disk layout information, to check that they are unchanged
|
||||
# for any future rebuilds
|
||||
_save_disk_layout(node, parse_instance_info(node))
|
||||
_save_disk_layout(node, deploy_utils.parse_instance_info(node))
|
||||
|
||||
destroy_images(node.uuid)
|
||||
return uuid_dict_returned
|
||||
@ -562,7 +452,7 @@ def validate(task):
|
||||
|
||||
# Validate the root device hints
|
||||
deploy_utils.parse_root_device_hints(task.node)
|
||||
parse_instance_info(task.node)
|
||||
deploy_utils.parse_instance_info(task.node)
|
||||
|
||||
|
||||
def validate_pass_bootloader_info_input(task, input_params):
|
||||
@ -972,21 +862,7 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
LOG.debug('Continuing the deployment on node %s', node.uuid)
|
||||
|
||||
uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)
|
||||
|
||||
if deploy_utils.get_boot_option(node) == "local":
|
||||
# Install the boot loader
|
||||
root_uuid = uuid_dict_returned.get('root uuid')
|
||||
efi_sys_uuid = uuid_dict_returned.get('efi system partition uuid')
|
||||
self.configure_local_boot(
|
||||
task, root_uuid=root_uuid,
|
||||
efi_system_part_uuid=efi_sys_uuid)
|
||||
|
||||
try:
|
||||
task.driver.boot.prepare_instance(task)
|
||||
except Exception as e:
|
||||
LOG.error(_LE('Deploy failed for instance %(instance)s. '
|
||||
'Error: %(error)s'),
|
||||
{'instance': node.instance_uuid, 'error': e})
|
||||
msg = _('Failed to continue agent deployment.')
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
root_uuid = uuid_dict_returned.get('root uuid')
|
||||
efi_sys_uuid = uuid_dict_returned.get('efi system partition uuid')
|
||||
self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid)
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
@ -40,6 +40,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
u'c02d7f33c123/deploy_kernel',
|
||||
'aki_path': u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/'
|
||||
u'kernel',
|
||||
'ari_path': u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/'
|
||||
u'ramdisk',
|
||||
'pxe_append_params': 'test_param',
|
||||
'deployment_ari_path': u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7'
|
||||
u'f33c123/deploy_ramdisk',
|
||||
@ -50,8 +52,6 @@ class TestPXEUtils(db_base.DbTestCase):
|
||||
|
||||
self.pxe_options = {
|
||||
'deployment_key': '0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
||||
'ari_path': u'/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/'
|
||||
u'ramdisk',
|
||||
'iscsi_target_iqn': u'iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33'
|
||||
u'c123',
|
||||
'deployment_id': u'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
||||
|
@ -3,3 +3,12 @@ default deploy
|
||||
label deploy
|
||||
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_kernel
|
||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk text test_param ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=agent_ipmitool root_device=vendor=fake,size=123 coreos.configdrive=0
|
||||
|
||||
label boot_partition
|
||||
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel
|
||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk root={{ ROOT }} ro text test_param
|
||||
|
||||
label boot_whole_disk
|
||||
COM32 chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
|
||||
|
@ -55,6 +55,9 @@ class TestAgentMethods(db_base.DbTestCase):
|
||||
def test_build_instance_info_for_deploy_glance_image(self, glance_mock):
|
||||
i_info = self.node.instance_info
|
||||
i_info['image_source'] = '733d1c44-a2ea-414b-aca7-69decf20d810'
|
||||
driver_internal_info = self.node.driver_internal_info
|
||||
driver_internal_info['is_whole_disk_image'] = True
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
|
||||
@ -76,14 +79,82 @@ class TestAgentMethods(db_base.DbTestCase):
|
||||
glance_mock.return_value.swift_temp_url.assert_called_once_with(
|
||||
image_info)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'parse_instance_info', autospec=True)
|
||||
@mock.patch.object(image_service, 'GlanceImageService', autospec=True)
|
||||
def test_build_instance_info_for_deploy_glance_partition_image(
|
||||
self, glance_mock, parse_instance_info_mock):
|
||||
i_info = self.node.instance_info
|
||||
i_info['image_source'] = '733d1c44-a2ea-414b-aca7-69decf20d810'
|
||||
i_info['kernel'] = '13ce5a56-1de3-4916-b8b2-be778645d003'
|
||||
i_info['ramdisk'] = 'a5a370a8-1b39-433f-be63-2c7d708e4b4e'
|
||||
i_info['root_gb'] = 5
|
||||
i_info['swap_mb'] = 4
|
||||
i_info['ephemeral_gb'] = 0
|
||||
i_info['ephemeral_format'] = None
|
||||
i_info['configdrive'] = 'configdrive'
|
||||
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.instance_info = i_info
|
||||
self.node.save()
|
||||
|
||||
image_info = {'checksum': 'aa', 'disk_format': 'qcow2',
|
||||
'container_format': 'bare',
|
||||
'properties': {'kernel_id': 'kernel',
|
||||
'ramdisk_id': 'ramdisk'}}
|
||||
glance_mock.return_value.show = mock.MagicMock(spec_set=[],
|
||||
return_value=image_info)
|
||||
glance_obj_mock = glance_mock.return_value
|
||||
glance_obj_mock.swift_temp_url.return_value = 'temp-url'
|
||||
parse_instance_info_mock.return_value = {'swap_mb': 4}
|
||||
image_source = '733d1c44-a2ea-414b-aca7-69decf20d810'
|
||||
expected_i_info = {'root_gb': 5,
|
||||
'swap_mb': 4,
|
||||
'ephemeral_gb': 0,
|
||||
'ephemeral_format': None,
|
||||
'configdrive': 'configdrive',
|
||||
'image_source': image_source,
|
||||
'image_url': 'temp-url',
|
||||
'kernel': 'kernel',
|
||||
'ramdisk': 'ramdisk',
|
||||
'image_type': 'partition',
|
||||
'image_checksum': 'aa',
|
||||
'fake_password': 'fakepass',
|
||||
'image_container_format': 'bare',
|
||||
'image_disk_format': 'qcow2',
|
||||
'foo': 'bar'}
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
|
||||
info = agent.build_instance_info_for_deploy(task)
|
||||
|
||||
glance_mock.assert_called_once_with(version=2,
|
||||
context=task.context)
|
||||
glance_mock.return_value.show.assert_called_once_with(
|
||||
self.node.instance_info['image_source'])
|
||||
glance_mock.return_value.swift_temp_url.assert_called_once_with(
|
||||
image_info)
|
||||
image_type = task.node.instance_info.get('image_type')
|
||||
self.assertEqual('partition', image_type)
|
||||
self.assertEqual('kernel', info.get('kernel'))
|
||||
self.assertEqual('ramdisk', info.get('ramdisk'))
|
||||
self.assertEqual(expected_i_info, info)
|
||||
parse_instance_info_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||
autospec=True)
|
||||
def test_build_instance_info_for_deploy_nonglance_image(
|
||||
self, validate_href_mock):
|
||||
i_info = self.node.instance_info
|
||||
driver_internal_info = self.node.driver_internal_info
|
||||
i_info['image_source'] = 'http://image-ref'
|
||||
i_info['image_checksum'] = 'aa'
|
||||
i_info['root_gb'] = 10
|
||||
i_info['image_checksum'] = 'aa'
|
||||
driver_internal_info['is_whole_disk_image'] = True
|
||||
self.node.instance_info = i_info
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
self.node.save()
|
||||
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
@ -97,6 +168,51 @@ class TestAgentMethods(db_base.DbTestCase):
|
||||
validate_href_mock.assert_called_once_with(
|
||||
mock.ANY, 'http://image-ref')
|
||||
|
||||
@mock.patch.object(deploy_utils, 'parse_instance_info', autospec=True)
|
||||
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||
autospec=True)
|
||||
def test_build_instance_info_for_deploy_nonglance_partition_image(
|
||||
self, validate_href_mock, parse_instance_info_mock):
|
||||
i_info = self.node.instance_info
|
||||
driver_internal_info = self.node.driver_internal_info
|
||||
i_info['image_source'] = 'http://image-ref'
|
||||
i_info['kernel'] = 'http://kernel-ref'
|
||||
i_info['ramdisk'] = 'http://ramdisk-ref'
|
||||
i_info['image_checksum'] = 'aa'
|
||||
i_info['root_gb'] = 10
|
||||
driver_internal_info['is_whole_disk_image'] = False
|
||||
self.node.instance_info = i_info
|
||||
self.node.driver_internal_info = driver_internal_info
|
||||
self.node.save()
|
||||
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
validate_href_mock.side_effect = ['http://image-ref',
|
||||
'http://kernel-ref',
|
||||
'http://ramdisk-ref']
|
||||
parse_instance_info_mock.return_value = {'swap_mb': 5}
|
||||
expected_i_info = {'image_source': 'http://image-ref',
|
||||
'image_url': 'http://image-ref',
|
||||
'image_type': 'partition',
|
||||
'kernel': 'http://kernel-ref',
|
||||
'ramdisk': 'http://ramdisk-ref',
|
||||
'image_checksum': 'aa',
|
||||
'root_gb': 10,
|
||||
'swap_mb': 5,
|
||||
'fake_password': 'fakepass',
|
||||
'foo': 'bar'}
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=False) as task:
|
||||
|
||||
info = agent.build_instance_info_for_deploy(task)
|
||||
|
||||
self.assertEqual(self.node.instance_info['image_source'],
|
||||
info['image_url'])
|
||||
validate_href_mock.assert_called_once_with(
|
||||
mock.ANY, 'http://image-ref')
|
||||
self.assertEqual('partition', info.get('image_type'))
|
||||
self.assertEqual(expected_i_info, info)
|
||||
parse_instance_info_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||
autospec=True)
|
||||
def test_build_instance_info_for_deploy_nonsupported_image(
|
||||
@ -287,19 +403,6 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(images, 'image_show', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_agent_fail_partition_image(
|
||||
self, pxe_boot_validate_mock, show_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.driver.validate, task)
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
show_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(images, 'image_show', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_invalid_root_device_hints(
|
||||
@ -572,20 +675,80 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
}
|
||||
)
|
||||
|
||||
def test_continue_deploy_partition_image(self):
|
||||
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'] = 'configdrive'
|
||||
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"}'
|
||||
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',
|
||||
'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'
|
||||
}
|
||||
|
||||
client_mock = mock.MagicMock(spec_set=['prepare_image'])
|
||||
self.passthru._client = client_mock
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
self.passthru.continue_deploy(task)
|
||||
|
||||
client_mock.prepare_image.assert_called_with(task.node,
|
||||
expected_image_info)
|
||||
self.assertEqual(states.DEPLOYWAIT, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE,
|
||||
task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(agent.AgentVendorInterface, '_get_uuid_from_result',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.conductor.utils.node_set_boot_device', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance',
|
||||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance(self, clean_pxe_mock, check_deploy_mock,
|
||||
bootdev_mock, power_off_mock,
|
||||
get_power_state_mock, node_power_action_mock):
|
||||
prepare_mock, power_off_mock,
|
||||
get_power_state_mock, node_power_action_mock,
|
||||
uuid_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
uuid_mock.return_value = 'root_uuid'
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
@ -598,30 +761,83 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
|
||||
clean_pxe_mock.assert_called_once_with(task.driver.boot, task)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
bootdev_mock.assert_called_once_with(task, 'disk', persistent=True)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.REBOOT)
|
||||
self.assertFalse(prepare_mock.called)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
driver_int_info = task.node.driver_internal_info
|
||||
self.assertIsNone(driver_int_info.get('root_uuid_or_disk_id'))
|
||||
self.assertFalse(uuid_mock.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', autospec=True)
|
||||
@mock.patch.object(agent.AgentVendorInterface, '_get_uuid_from_result',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch('ironic.conductor.utils.node_set_boot_device', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance',
|
||||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_partition_image(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
prepare_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock, boot_mode_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.return_value = 'root_uuid'
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
boot_mode_mock.return_value = 'bios'
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.REBOOT)
|
||||
prepare_mock.assert_called_once_with(task.driver.boot, task)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
driver_int_info = task.node.driver_internal_info
|
||||
self.assertEqual(driver_int_info.get('root_uuid_or_disk_id'),
|
||||
'root_uuid')
|
||||
uuid_mock.assert_called_once_with(self.passthru, task, 'root_uuid')
|
||||
boot_mode_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(agent.AgentVendorInterface, '_get_uuid_from_result',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance',
|
||||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_boot_none(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
bootdev_mock, power_off_mock,
|
||||
prepare_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock):
|
||||
node_power_action_mock,
|
||||
uuid_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
@ -634,8 +850,96 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
self.assertFalse(prepare_mock.called)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
bootdev_mock.assert_called_once_with(task, 'disk', persistent=True)
|
||||
driver_int_info = task.node.driver_internal_info
|
||||
self.assertIsNone(driver_int_info.get('root_uuid_or_disk_id'))
|
||||
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
task, states.REBOOT)
|
||||
self.assertEqual(states.ACTIVE, task.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, task.node.target_provision_state)
|
||||
self.assertFalse(uuid_mock.called)
|
||||
|
||||
@mock.patch.object(agent.AgentVendorInterface, '_get_uuid_from_result',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance',
|
||||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_boot_error(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
prepare_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock):
|
||||
check_deploy_mock.return_value = "Error"
|
||||
uuid_mock.return_value = None
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.boot = None
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
self.assertFalse(prepare_mock.called)
|
||||
self.assertFalse(power_off_mock.called)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
'configure_local_boot', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
|
||||
@mock.patch.object(agent.AgentVendorInterface, '_get_uuid_from_result',
|
||||
autospec=True)
|
||||
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
|
||||
@mock.patch.object(fake.FakePower, 'get_power_state',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(agent_client.AgentClient, 'power_off',
|
||||
spec=types.FunctionType)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance',
|
||||
autospec=True)
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.check_deploy_success', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk', autospec=True)
|
||||
def test_reboot_to_instance_localboot(self, clean_pxe_mock,
|
||||
check_deploy_mock,
|
||||
prepare_mock, power_off_mock,
|
||||
get_power_state_mock,
|
||||
node_power_action_mock,
|
||||
uuid_mock,
|
||||
bootdev_mock,
|
||||
configure_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
uuid_mock.side_effect = ['root_uuid', 'efi_uuid']
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
get_power_state_mock.return_value = states.POWER_OFF
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
boot_option = {'capabilities': '{"boot_option": "local"}'}
|
||||
task.node.instance_info = boot_option
|
||||
self.passthru.reboot_to_instance(task)
|
||||
|
||||
self.assertFalse(clean_pxe_mock.called)
|
||||
check_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||
self.assertFalse(bootdev_mock.called)
|
||||
power_off_mock.assert_called_once_with(task.node)
|
||||
get_power_state_mock.assert_called_once_with(task)
|
||||
node_power_action_mock.assert_called_once_with(
|
||||
|
@ -29,6 +29,7 @@ from ironic.drivers.modules import agent_base_vendor
|
||||
from ironic.drivers.modules import agent_client
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules import fake
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic import objects
|
||||
from ironic.tests.unit.conductor import mgr_utils
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
@ -791,6 +792,93 @@ class TestBaseAgentVendor(db_base.DbTestCase):
|
||||
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'set_failed_state', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'get_boot_option', autospec=True)
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
'configure_local_boot', autospec=True)
|
||||
def test_prepare_instance_to_boot_netboot(self, configure_mock,
|
||||
boot_option_mock,
|
||||
prepare_instance_mock,
|
||||
failed_state_mock):
|
||||
boot_option_mock.return_value = 'netboot'
|
||||
prepare_instance_mock.return_value = None
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
root_uuid = 'root_uuid'
|
||||
efi_system_part_uuid = 'efi_sys_uuid'
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.passthru.prepare_instance_to_boot(task, root_uuid,
|
||||
efi_system_part_uuid)
|
||||
self.assertFalse(configure_mock.called)
|
||||
boot_option_mock.assert_called_once_with(task.node)
|
||||
prepare_instance_mock.assert_called_once_with(task.driver.boot,
|
||||
task)
|
||||
self.assertFalse(failed_state_mock.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'set_failed_state', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'get_boot_option', autospec=True)
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
'configure_local_boot', autospec=True)
|
||||
def test_prepare_instance_to_boot_localboot(self, configure_mock,
|
||||
boot_option_mock,
|
||||
prepare_instance_mock,
|
||||
failed_state_mock):
|
||||
boot_option_mock.return_value = 'local'
|
||||
prepare_instance_mock.return_value = None
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
root_uuid = 'root_uuid'
|
||||
efi_system_part_uuid = 'efi_sys_uuid'
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.passthru.prepare_instance_to_boot(task, root_uuid,
|
||||
efi_system_part_uuid)
|
||||
configure_mock.assert_called_once_with(self.passthru, task,
|
||||
root_uuid,
|
||||
efi_system_part_uuid)
|
||||
boot_option_mock.assert_called_once_with(task.node)
|
||||
prepare_instance_mock.assert_called_once_with(task.driver.boot,
|
||||
task)
|
||||
self.assertFalse(failed_state_mock.called)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'set_failed_state', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True)
|
||||
@mock.patch.object(deploy_utils, 'get_boot_option', autospec=True)
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
'configure_local_boot', autospec=True)
|
||||
def test_prepare_instance_to_boot_configure_fails(self, configure_mock,
|
||||
boot_option_mock,
|
||||
prepare_mock,
|
||||
failed_state_mock):
|
||||
boot_option_mock.return_value = 'local'
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
root_uuid = 'root_uuid'
|
||||
efi_system_part_uuid = 'efi_sys_uuid'
|
||||
reason = 'reason'
|
||||
configure_mock.side_effect = (
|
||||
exception.InstanceDeployFailure(reason=reason))
|
||||
prepare_mock.side_effect = (
|
||||
exception.InstanceDeployFailure(reason=reason))
|
||||
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
self.passthru.prepare_instance_to_boot, task,
|
||||
root_uuid, efi_system_part_uuid)
|
||||
configure_mock.assert_called_once_with(self.passthru, task,
|
||||
root_uuid,
|
||||
efi_system_part_uuid)
|
||||
boot_option_mock.assert_called_once_with(task.node)
|
||||
self.assertFalse(prepare_mock.called)
|
||||
self.assertFalse(failed_state_mock.called)
|
||||
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
'notify_conductor_resume_clean', autospec=True)
|
||||
@mock.patch.object(agent_client.AgentClient, 'get_commands_status',
|
||||
|
@ -61,7 +61,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT
|
||||
)
|
||||
info = iscsi_deploy.parse_instance_info(node)
|
||||
info = deploy_utils.parse_instance_info(node)
|
||||
self.assertIsNotNone(info.get('image_source'))
|
||||
self.assertIsNotNone(info.get('root_gb'))
|
||||
self.assertEqual(0, info.get('ephemeral_gb'))
|
||||
@ -76,7 +76,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test_parse_instance_info_missing_root_gb(self):
|
||||
@ -89,7 +89,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test_parse_instance_info_invalid_root_gb(self):
|
||||
@ -100,7 +100,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test_parse_instance_info_valid_ephemeral_gb(self):
|
||||
@ -113,10 +113,22 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
data = deploy_utils.parse_instance_info(node)
|
||||
self.assertEqual(ephemeral_gb, data.get('ephemeral_gb'))
|
||||
self.assertEqual(ephemeral_fmt, data.get('ephemeral_format'))
|
||||
|
||||
def test_parse_instance_info_unicode_swap_mb(self):
|
||||
swap_mb = u'10'
|
||||
swap_mb_int = 10
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['swap_mb'] = swap_mb
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = deploy_utils.parse_instance_info(node)
|
||||
self.assertEqual(swap_mb_int, data.get('swap_mb'))
|
||||
|
||||
def test_parse_instance_info_invalid_ephemeral_gb(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['ephemeral_gb'] = 'foobar'
|
||||
@ -127,7 +139,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test_parse_instance_info_valid_ephemeral_missing_format(self):
|
||||
@ -141,7 +153,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
instance_info = iscsi_deploy.parse_instance_info(node)
|
||||
instance_info = deploy_utils.parse_instance_info(node)
|
||||
self.assertEqual(ephemeral_fmt, instance_info['ephemeral_format'])
|
||||
|
||||
def test_parse_instance_info_valid_preserve_ephemeral_true(self):
|
||||
@ -155,7 +167,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
data = deploy_utils.parse_instance_info(node)
|
||||
self.assertTrue(data.get('preserve_ephemeral'))
|
||||
|
||||
def test_parse_instance_info_valid_preserve_ephemeral_false(self):
|
||||
@ -168,7 +180,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
data = deploy_utils.parse_instance_info(node)
|
||||
self.assertFalse(data.get('preserve_ephemeral'))
|
||||
|
||||
def test_parse_instance_info_invalid_preserve_ephemeral(self):
|
||||
@ -179,7 +191,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test_parse_instance_info_invalid_ephemeral_disk(self):
|
||||
@ -197,7 +209,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=drv_internal_dict,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
deploy_utils.parse_instance_info,
|
||||
node)
|
||||
|
||||
def test__check_disk_layout_unchanged_fails(self):
|
||||
@ -215,7 +227,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=drv_internal_dict,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy._check_disk_layout_unchanged,
|
||||
deploy_utils._check_disk_layout_unchanged,
|
||||
node, info)
|
||||
|
||||
def test__check_disk_layout_unchanged(self):
|
||||
@ -232,7 +244,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=drv_internal_dict,
|
||||
)
|
||||
self.assertIsNone(iscsi_deploy._check_disk_layout_unchanged(node,
|
||||
self.assertIsNone(deploy_utils._check_disk_layout_unchanged(node,
|
||||
info))
|
||||
|
||||
def test__save_disk_layout(self):
|
||||
@ -259,7 +271,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
instance_info = iscsi_deploy.parse_instance_info(node)
|
||||
instance_info = deploy_utils.parse_instance_info(node)
|
||||
self.assertEqual('http://1.2.3.4/cd', instance_info['configdrive'])
|
||||
|
||||
def test_parse_instance_info_nonglance_image(self):
|
||||
@ -271,7 +283,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
iscsi_deploy.parse_instance_info(node)
|
||||
deploy_utils.parse_instance_info(node)
|
||||
|
||||
def test_parse_instance_info_nonglance_image_no_kernel(self):
|
||||
info = INST_INFO_DICT.copy()
|
||||
@ -282,7 +294,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info, node)
|
||||
deploy_utils.parse_instance_info, node)
|
||||
|
||||
def test_parse_instance_info_whole_disk_image(self):
|
||||
driver_internal_info = dict(DRV_INTERNAL_INFO_DICT)
|
||||
@ -291,7 +303,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
self.context, instance_info=INST_INFO_DICT,
|
||||
driver_internal_info=driver_internal_info,
|
||||
)
|
||||
instance_info = iscsi_deploy.parse_instance_info(node)
|
||||
instance_info = deploy_utils.parse_instance_info(node)
|
||||
self.assertIsNotNone(instance_info.get('image_source'))
|
||||
self.assertIsNotNone(instance_info.get('root_gb'))
|
||||
self.assertEqual(0, instance_info.get('swap_mb'))
|
||||
@ -303,7 +315,7 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
del info['root_gb']
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info, node)
|
||||
deploy_utils.parse_instance_info, node)
|
||||
|
||||
|
||||
class IscsiDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Adds support for partition images for agent
|
||||
based drivers.
|
Loading…
Reference in New Issue
Block a user