Merge "Adds support for deploying whole disk images"
This commit is contained in:
commit
dc08463ec1
@ -513,6 +513,13 @@ them to Glance service:
|
||||
kernel_id=$MY_VMLINUZ_UUID --property \
|
||||
ramdisk_id=$MY_INITRD_UUID < my-image.qcow2
|
||||
|
||||
- *Note:* To deploy a whole disk image, a kernel_id and a ramdisk_id
|
||||
shouldn't be associated with the image. An example is as follows::
|
||||
|
||||
glance image-create --name my-whole-disk-image --is-public True \
|
||||
--disk-format qcow2 \
|
||||
--container-format bare < my-whole-disk-image.qcow2
|
||||
|
||||
3. Add the deploy images to glance
|
||||
|
||||
Add the *my-deploy-ramdisk.kernel* and
|
||||
@ -612,6 +619,19 @@ node(s) where ``ironic-conductor`` is running.
|
||||
Ubuntu (14.10 and after):
|
||||
sudo cp /usr/lib/PXELINUX/pxelinux.0 /tftpboot
|
||||
|
||||
#. If whole disk images need to be deployed via PXE-netboot, copy the
|
||||
chain.c32 image to ``/tftpboot`` to support it. The chain.c32 image
|
||||
might be found at::
|
||||
|
||||
Ubuntu (Up to and including 14.04):
|
||||
sudo cp /usr/lib/syslinux/chain.c32 /tftpboot
|
||||
|
||||
Ubuntu (14.10 and after):
|
||||
sudo cp /usr/lib/syslinux/modules/bios/chain.c32 /tftpboot
|
||||
|
||||
Fedora:
|
||||
sudo cp /boot/extlinux/chain.c32 /tftpboot
|
||||
|
||||
#. If the version of syslinux is **greater than** 4 we also need to make sure
|
||||
that we copy the library modules into the ``/tftpboot`` directory [2]_
|
||||
[1]_::
|
||||
|
@ -6,6 +6,7 @@
|
||||
iscsiadm: CommandFilter, iscsiadm, root
|
||||
blkid: CommandFilter, blkid, root
|
||||
blockdev: CommandFilter, blockdev, root
|
||||
hexdump: CommandFilter, hexdump, root
|
||||
|
||||
# ironic/common/utils.py
|
||||
mkswap: CommandFilter, mkswap, root
|
||||
|
@ -35,6 +35,11 @@ LOG = logging.getLogger(__name__)
|
||||
IMAGE_CHUNK_SIZE = 1024 * 1024 # 1mb
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
# Import this opt early so that it is available when registering
|
||||
# glance_opts below.
|
||||
CONF.import_opt('my_ip', 'ironic.netconf')
|
||||
|
||||
glance_opts = [
|
||||
cfg.StrOpt('glance_host',
|
||||
default='$my_ip',
|
||||
@ -64,8 +69,6 @@ glance_opts = [
|
||||
'Set to https for SSL.'),
|
||||
]
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(glance_opts, group='glance')
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ from oslo_concurrency import processutils
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils as glance_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common import image_service as service
|
||||
@ -377,3 +378,33 @@ def create_boot_iso(context, output_filename, kernel_href,
|
||||
|
||||
create_isolinux_image(output_filename, kernel_path,
|
||||
ramdisk_path, params)
|
||||
|
||||
|
||||
def is_whole_disk_image(ctx, instance_info):
|
||||
"""Find out if the image is a partition image or a whole disk image.
|
||||
|
||||
:param ctx: an admin context
|
||||
:param instance_info: a node's instance info dict
|
||||
|
||||
:returns True for whole disk images and False for partition images
|
||||
and None on no image_source or Error.
|
||||
"""
|
||||
image_source = instance_info.get('image_source')
|
||||
if not image_source:
|
||||
return
|
||||
|
||||
is_whole_disk_image = False
|
||||
if glance_utils.is_glance_image(image_source):
|
||||
try:
|
||||
iproperties = get_image_properties(ctx, image_source)
|
||||
except Exception:
|
||||
return
|
||||
is_whole_disk_image = (not iproperties.get('kernel_id') and
|
||||
not iproperties.get('ramdisk_id'))
|
||||
else:
|
||||
# Non glance image ref
|
||||
if (not instance_info.get('kernel') and
|
||||
not instance_info.get('ramdisk')):
|
||||
is_whole_disk_image = True
|
||||
|
||||
return is_whole_disk_image
|
||||
|
@ -68,7 +68,9 @@ def _build_pxe_config(pxe_options, template):
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path))
|
||||
template = env.get_template(tmpl_file)
|
||||
return template.render({'pxe_options': pxe_options,
|
||||
'ROOT': '{{ ROOT }}'})
|
||||
'ROOT': '{{ ROOT }}',
|
||||
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}',
|
||||
})
|
||||
|
||||
|
||||
def _link_mac_pxe_configs(task):
|
||||
|
@ -66,6 +66,7 @@ from ironic.common.i18n import _LC
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.common import images
|
||||
from ironic.common import keystone
|
||||
from ironic.common import rpc
|
||||
from ironic.common import states
|
||||
@ -667,14 +668,6 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
if node.maintenance:
|
||||
raise exception.NodeInMaintenance(op=_('provisioning'),
|
||||
node=node.uuid)
|
||||
try:
|
||||
task.driver.power.validate(task)
|
||||
task.driver.deploy.validate(task)
|
||||
except (exception.InvalidParameterValue,
|
||||
exception.MissingParameterValue) as e:
|
||||
raise exception.InstanceDeployFailure(_(
|
||||
"RPC do_node_deploy failed to validate deploy or "
|
||||
"power info. Error: %(msg)s") % {'msg': e})
|
||||
|
||||
if rebuild:
|
||||
event = 'rebuild'
|
||||
@ -689,10 +682,29 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
instance_info.pop('kernel', None)
|
||||
instance_info.pop('ramdisk', None)
|
||||
node.instance_info = instance_info
|
||||
node.save()
|
||||
else:
|
||||
event = 'deploy'
|
||||
|
||||
driver_internal_info = node.driver_internal_info
|
||||
# Infer the image type to make sure the deploy driver
|
||||
# validates only the necessary variables for different
|
||||
# image types.
|
||||
# NOTE(sirushtim): The iwdi variable can be None. It's upto
|
||||
# the deploy driver to validate this.
|
||||
iwdi = images.is_whole_disk_image(context, node.instance_info)
|
||||
driver_internal_info['is_whole_disk_image'] = iwdi
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
|
||||
try:
|
||||
task.driver.power.validate(task)
|
||||
task.driver.deploy.validate(task)
|
||||
except (exception.InvalidParameterValue,
|
||||
exception.MissingParameterValue) as e:
|
||||
raise exception.InstanceDeployFailure(_(
|
||||
"RPC do_node_deploy failed to validate deploy or "
|
||||
"power info. Error: %(msg)s") % {'msg': e})
|
||||
|
||||
LOG.debug("do_node_deploy Calling event: %(event)s for node: "
|
||||
"%(node)s", {'event': event, 'node': node.uuid})
|
||||
try:
|
||||
@ -1040,6 +1052,15 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
node_id)
|
||||
ret_dict = {}
|
||||
with task_manager.acquire(context, node_id, shared=True) as task:
|
||||
# NOTE(sirushtim): the is_whole_disk_image variable is needed by
|
||||
# deploy drivers for doing their validate(). Since the deploy
|
||||
# isn't being done yet and the driver information could change in
|
||||
# the meantime, we don't know if the is_whole_disk_image value will
|
||||
# change or not. It isn't saved to the DB, but only used with this
|
||||
# node instance for the current validations.
|
||||
iwdi = images.is_whole_disk_image(context,
|
||||
task.node.instance_info)
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = iwdi
|
||||
for iface_name in (task.driver.core_interfaces +
|
||||
task.driver.standard_interfaces):
|
||||
iface = getattr(task.driver, iface_name, None)
|
||||
|
@ -360,13 +360,14 @@ class BaseAgentVendor(base.VendorInterface):
|
||||
on encountering error while setting the boot device on the node.
|
||||
"""
|
||||
node = task.node
|
||||
result = self._client.install_bootloader(node, root_uuid)
|
||||
if result['command_status'] == 'FAILED':
|
||||
msg = (_("Failed to install a bootloader when "
|
||||
"deploying node %(node)s. Error: %(error)s") %
|
||||
{'node': node.uuid,
|
||||
'error': result['command_error']})
|
||||
self._log_and_raise_deployment_error(task, msg)
|
||||
if not node.driver_internal_info.get('is_whole_disk_image'):
|
||||
result = self._client.install_bootloader(node, root_uuid)
|
||||
if result['command_status'] == 'FAILED':
|
||||
msg = (_("Failed to install a bootloader when "
|
||||
"deploying node %(node)s. Error: %(error)s") %
|
||||
{'node': node.uuid,
|
||||
'error': result['command_error']})
|
||||
self._log_and_raise_deployment_error(task, msg)
|
||||
|
||||
try:
|
||||
deploy_utils.try_set_boot_device(task, boot_devices.DISK)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
|
||||
import base64
|
||||
import contextlib
|
||||
import gzip
|
||||
import math
|
||||
import os
|
||||
@ -185,6 +186,28 @@ def delete_iscsi(portal_address, portal_port, target_iqn):
|
||||
delay_on_retry=True)
|
||||
|
||||
|
||||
def get_disk_identifier(dev):
|
||||
"""Get the disk identifier from the disk being exposed by the ramdisk.
|
||||
|
||||
This disk identifier is appended to the pxe config which will then be
|
||||
used by chain.c32 to detect the correct disk to chainload. This is helpful
|
||||
in deployments to nodes with multiple disks.
|
||||
|
||||
http://www.syslinux.org/wiki/index.php/Comboot/chain.c32#mbr:
|
||||
|
||||
:param dev: Path for the already populated disk device.
|
||||
:returns The Disk Identifier.
|
||||
"""
|
||||
disk_identifier = utils.execute('hexdump', '-s', '440', '-n', '4',
|
||||
'-e', '''\"0x%08x\"''',
|
||||
dev,
|
||||
run_as_root=True,
|
||||
check_exit_code=[0],
|
||||
attempts=5,
|
||||
delay_on_retry=True)
|
||||
return disk_identifier[0]
|
||||
|
||||
|
||||
def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
configdrive_mb, commit=True, boot_option="netboot"):
|
||||
"""Partition the disk device.
|
||||
@ -290,28 +313,63 @@ def block_uuid(dev):
|
||||
return out.strip()
|
||||
|
||||
|
||||
def switch_pxe_config(path, root_uuid, boot_mode):
|
||||
"""Switch a pxe config from deployment mode to service mode."""
|
||||
def _replace_lines_in_file(path, regex_pattern, replacement):
|
||||
with open(path) as f:
|
||||
lines = f.readlines()
|
||||
root = 'UUID=%s' % root_uuid
|
||||
rre = re.compile(r'\{\{ ROOT \}\}')
|
||||
|
||||
if boot_mode == 'uefi':
|
||||
dre = re.compile('^default=.*$')
|
||||
boot_line = 'default=boot'
|
||||
else:
|
||||
pxe_cmd = 'goto' if CONF.pxe.ipxe_enabled else 'default'
|
||||
dre = re.compile('^%s .*$' % pxe_cmd)
|
||||
boot_line = '%s boot' % pxe_cmd
|
||||
|
||||
compiled_pattern = re.compile(regex_pattern)
|
||||
with open(path, 'w') as f:
|
||||
for line in lines:
|
||||
line = rre.sub(root, line)
|
||||
line = dre.sub(boot_line, line)
|
||||
line = compiled_pattern.sub(replacement, line)
|
||||
f.write(line)
|
||||
|
||||
|
||||
def _replace_root_uuid(path, root_uuid):
|
||||
root = 'UUID=%s' % root_uuid
|
||||
pattern = r'\{\{ ROOT \}\}'
|
||||
_replace_lines_in_file(path, pattern, root)
|
||||
|
||||
|
||||
def _replace_boot_line(path, boot_mode, is_whole_disk_image):
|
||||
if is_whole_disk_image:
|
||||
boot_disk_type = 'boot_whole_disk'
|
||||
else:
|
||||
boot_disk_type = 'boot_partition'
|
||||
|
||||
if boot_mode == 'uefi':
|
||||
pattern = '^default=.*$'
|
||||
boot_line = 'default=%s' % boot_disk_type
|
||||
else:
|
||||
pxe_cmd = 'goto' if CONF.pxe.ipxe_enabled else 'default'
|
||||
pattern = '^%s .*$' % pxe_cmd
|
||||
boot_line = '%s %s' % (pxe_cmd, boot_disk_type)
|
||||
|
||||
_replace_lines_in_file(path, pattern, boot_line)
|
||||
|
||||
|
||||
def _replace_disk_identifier(path, disk_identifier):
|
||||
pattern = r'\{\{ DISK_IDENTIFIER \}\}'
|
||||
_replace_lines_in_file(path, pattern, disk_identifier)
|
||||
|
||||
|
||||
def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
||||
is_whole_disk_image):
|
||||
"""Switch a pxe config from deployment mode to service mode.
|
||||
|
||||
:param path: path to the pxe config file in tftpboot.
|
||||
:param root_uuid_or_disk_id: root uuid in case of partition image or
|
||||
disk_id in case of whole disk image.
|
||||
:param boot_mode: if boot mode is uefi or bios.
|
||||
:param is_whole_disk_image: if the image is a whole disk image or not.
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
def notify(address, port):
|
||||
"""Notify a node that it becomes ready to reboot."""
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@ -480,10 +538,6 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
:param boot_option: Can be "local" or "netboot". "netboot" by default.
|
||||
:returns: the UUID of the root partition.
|
||||
"""
|
||||
if not is_block_device(dev):
|
||||
raise exception.InstanceDeployFailure(
|
||||
_("Parent device '%s' not found") % dev)
|
||||
|
||||
# the only way for preserve_ephemeral to be set to true is if we are
|
||||
# rebuilding an instance with --preserve_ephemeral.
|
||||
commit = not preserve_ephemeral
|
||||
@ -550,11 +604,11 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
return root_uuid
|
||||
|
||||
|
||||
def deploy(address, port, iqn, lun, image_path,
|
||||
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"):
|
||||
"""All-in-one function to deploy a node.
|
||||
"""All-in-one function to deploy a partition image to a node.
|
||||
|
||||
:param address: The iSCSI IP address.
|
||||
:param port: The iSCSI port number.
|
||||
@ -576,18 +630,60 @@ def deploy(address, port, iqn, lun, image_path,
|
||||
:param boot_option: Can be "local" or "netboot". "netboot" by default.
|
||||
:returns: the UUID of the root partition.
|
||||
"""
|
||||
dev = get_dev(address, port, iqn, lun)
|
||||
image_mb = get_image_mb(image_path)
|
||||
if image_mb > root_mb:
|
||||
root_mb = image_mb
|
||||
discovery(address, port)
|
||||
login_iscsi(address, port, iqn)
|
||||
try:
|
||||
with _iscsi_setup_and_handle_errors(address, port, iqn,
|
||||
lun, image_path) as dev:
|
||||
image_mb = get_image_mb(image_path)
|
||||
if image_mb > root_mb:
|
||||
root_mb = image_mb
|
||||
|
||||
root_uuid = work_on_disk(dev, root_mb, swap_mb, ephemeral_mb,
|
||||
ephemeral_format, image_path, node_uuid,
|
||||
preserve_ephemeral=preserve_ephemeral,
|
||||
configdrive=configdrive,
|
||||
boot_option=boot_option)
|
||||
|
||||
return root_uuid
|
||||
|
||||
|
||||
def deploy_disk_image(address, port, iqn, lun,
|
||||
image_path, node_uuid):
|
||||
"""All-in-one function to deploy a whole disk image to a node.
|
||||
|
||||
:param address: The iSCSI IP address.
|
||||
:param port: The iSCSI port number.
|
||||
:param iqn: The iSCSI qualified name.
|
||||
:param lun: The iSCSI logical unit number.
|
||||
:param image_path: Path for the instance's disk image.
|
||||
:param node_uuid: node's uuid. Used for logging. Currently not in use
|
||||
by this function but could be used in the future.
|
||||
"""
|
||||
with _iscsi_setup_and_handle_errors(address, port, iqn,
|
||||
lun, image_path) as dev:
|
||||
populate_image(image_path, dev)
|
||||
disk_identifier = get_disk_identifier(dev)
|
||||
|
||||
return disk_identifier
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _iscsi_setup_and_handle_errors(address, port, iqn, lun,
|
||||
image_path):
|
||||
"""Function that yields an iSCSI target device to work on.
|
||||
|
||||
:param address: The iSCSI IP address.
|
||||
:param port: The iSCSI port number.
|
||||
:param iqn: The iSCSI qualified name.
|
||||
:param lun: The iSCSI logical unit number.
|
||||
:param image_path: Path for the instance's disk image.
|
||||
"""
|
||||
dev = get_dev(address, port, iqn, lun)
|
||||
discovery(address, port)
|
||||
login_iscsi(address, port, iqn)
|
||||
if not is_block_device(dev):
|
||||
raise exception.InstanceDeployFailure(_("Parent device '%s' not found")
|
||||
% dev)
|
||||
try:
|
||||
yield dev
|
||||
except processutils.ProcessExecutionError as err:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Deploy to address %s failed."), address)
|
||||
@ -602,8 +698,6 @@ def deploy(address, port, iqn, lun, image_path,
|
||||
logout_iscsi(address, port, iqn)
|
||||
delete_iscsi(address, port, iqn)
|
||||
|
||||
return root_uuid
|
||||
|
||||
|
||||
def notify_deploy_complete(address):
|
||||
"""Notifies the completion of deployment to the baremetal node.
|
||||
|
@ -7,6 +7,10 @@ image={{pxe_options.deployment_aki_path}}
|
||||
|
||||
|
||||
image={{pxe_options.aki_path}}
|
||||
label=boot
|
||||
label=boot_partition
|
||||
initrd={{pxe_options.ari_path}}
|
||||
append="root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }} ip=%I:{{pxe_options.tftp_server}}:%G:%M:%H::on"
|
||||
|
||||
image=chain.c32
|
||||
label=boot_whole_disk
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
|
@ -10,7 +10,12 @@ kernel {{ pxe_options.deployment_aki_path }} selinux=0 disk={{ pxe_options.disk
|
||||
initrd {{ pxe_options.deployment_ari_path }}
|
||||
boot
|
||||
|
||||
:boot
|
||||
:boot_partition
|
||||
kernel {{ pxe_options.aki_path }} root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }}
|
||||
initrd {{ pxe_options.ari_path }}
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
kernel chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
boot
|
||||
|
@ -114,10 +114,12 @@ def parse_instance_info(node):
|
||||
info = node.instance_info
|
||||
i_info = {}
|
||||
i_info['image_source'] = info.get('image_source')
|
||||
if (i_info['image_source'] and
|
||||
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['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"
|
||||
@ -129,18 +131,26 @@ def parse_instance_info(node):
|
||||
|
||||
i_info['swap_mb'] = info.get('swap_mb', 0)
|
||||
i_info['ephemeral_gb'] = info.get('ephemeral_gb', 0)
|
||||
i_info['ephemeral_format'] = info.get('ephemeral_format')
|
||||
i_info['configdrive'] = info.get('configdrive')
|
||||
|
||||
err_msg_invalid = _("Cannot validate parameter for iSCSI deploy. "
|
||||
"Invalid parameter %(param)s. Reason: %(reason)s")
|
||||
for param in ('root_gb', 'swap_mb', 'ephemeral_gb'):
|
||||
try:
|
||||
int(i_info[param])
|
||||
except ValueError:
|
||||
reason = _("'%s' is not an integer value.") % i_info[param]
|
||||
reason = _("%s is not an integer value.") % i_info[param]
|
||||
raise exception.InvalidParameterValue(err_msg_invalid %
|
||||
{'param': param, 'reason': reason})
|
||||
{'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
|
||||
@ -228,12 +238,15 @@ def get_deploy_info(node, **kwargs):
|
||||
'iqn': kwargs.get('iqn'),
|
||||
'lun': kwargs.get('lun', '1'),
|
||||
'image_path': _get_image_file_path(node.uuid),
|
||||
'root_mb': 1024 * int(i_info['root_gb']),
|
||||
'swap_mb': int(i_info['swap_mb']),
|
||||
'ephemeral_mb': 1024 * int(i_info['ephemeral_gb']),
|
||||
'preserve_ephemeral': i_info['preserve_ephemeral'],
|
||||
'node_uuid': node.uuid,
|
||||
'boot_option': get_boot_option(node)}
|
||||
'node_uuid': node.uuid}
|
||||
|
||||
is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
|
||||
if not is_whole_disk_image:
|
||||
params.update({'root_mb': 1024 * int(i_info['root_gb']),
|
||||
'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)})
|
||||
|
||||
missing = [key for key in params if params[key] is None]
|
||||
if missing:
|
||||
@ -241,6 +254,9 @@ def get_deploy_info(node, **kwargs):
|
||||
"Parameters %s were not passed to ironic"
|
||||
" for deploy.") % missing)
|
||||
|
||||
if is_whole_disk_image:
|
||||
return params
|
||||
|
||||
# configdrive and ephemeral_format are nullable
|
||||
params['ephemeral_format'] = i_info.get('ephemeral_format')
|
||||
params['configdrive'] = i_info.get('configdrive')
|
||||
@ -258,7 +274,8 @@ def continue_deploy(task, **kwargs):
|
||||
:param kwargs: the kwargs to be passed to deploy.
|
||||
:raises: InvalidState if the event is not allowed by the associated
|
||||
state machine.
|
||||
:returns: UUID of the root partition or None on error.
|
||||
:returns: UUID of the root partition for partition images or disk
|
||||
identifier for whole disk images or None on error.
|
||||
"""
|
||||
node = task.node
|
||||
|
||||
@ -283,9 +300,13 @@ def continue_deploy(task, **kwargs):
|
||||
LOG.debug('Continuing deployment for node %(node)s, params %(params)s',
|
||||
{'node': node.uuid, 'params': log_params})
|
||||
|
||||
root_uuid = None
|
||||
root_uuid_or_disk_id = None
|
||||
try:
|
||||
root_uuid = deploy_utils.deploy(**params)
|
||||
if node.driver_internal_info['is_whole_disk_image']:
|
||||
root_uuid_or_disk_id = deploy_utils.deploy_disk_image(**params)
|
||||
else:
|
||||
root_uuid_or_disk_id = deploy_utils.deploy_partition_image(
|
||||
**params)
|
||||
except Exception as e:
|
||||
LOG.error(_LE('Deploy failed for instance %(instance)s. '
|
||||
'Error: %(error)s'),
|
||||
@ -294,7 +315,7 @@ def continue_deploy(task, **kwargs):
|
||||
'iSCSI deployment.'))
|
||||
|
||||
destroy_images(node.uuid)
|
||||
return root_uuid
|
||||
return root_uuid_or_disk_id
|
||||
|
||||
|
||||
def do_agent_iscsi_deploy(task, agent_client):
|
||||
@ -337,21 +358,22 @@ def do_agent_iscsi_deploy(task, agent_client):
|
||||
'key': iscsi_options['deployment_key'],
|
||||
'address': address}
|
||||
|
||||
root_uuid = continue_deploy(task, **iscsi_params)
|
||||
if not root_uuid:
|
||||
msg = (_("Couldn't determine the UUID of the root partition "
|
||||
"when deploying node %s") % node.uuid)
|
||||
root_uuid_or_disk_id = continue_deploy(task, **iscsi_params)
|
||||
if not root_uuid_or_disk_id:
|
||||
msg = (_("Couldn't determine the UUID of the root "
|
||||
"partition or the disk identifier when deploying "
|
||||
"node %s") % node.uuid)
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
raise exception.InstanceDeployFailure(reason=msg)
|
||||
|
||||
# TODO(lucasagomes): Move this bit saving the root_uuid to
|
||||
# iscsi_deploy.continue_deploy()
|
||||
driver_internal_info = node.driver_internal_info
|
||||
driver_internal_info['root_uuid'] = root_uuid
|
||||
driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
|
||||
return root_uuid
|
||||
return root_uuid_or_disk_id
|
||||
|
||||
|
||||
def parse_root_device_hints(node):
|
||||
|
@ -185,28 +185,34 @@ def _build_pxe_config_options(node, pxe_info, ctx):
|
||||
:returns: A dictionary of pxe options to be used in the pxe bootfile
|
||||
template.
|
||||
"""
|
||||
is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
|
||||
|
||||
if CONF.pxe.ipxe_enabled:
|
||||
deploy_kernel = '/'.join([CONF.pxe.http_url, node.uuid,
|
||||
'deploy_kernel'])
|
||||
deploy_ramdisk = '/'.join([CONF.pxe.http_url, node.uuid,
|
||||
'deploy_ramdisk'])
|
||||
kernel = '/'.join([CONF.pxe.http_url, node.uuid, 'kernel'])
|
||||
ramdisk = '/'.join([CONF.pxe.http_url, node.uuid, 'ramdisk'])
|
||||
if not is_whole_disk_image:
|
||||
kernel = '/'.join([CONF.pxe.http_url, node.uuid, 'kernel'])
|
||||
ramdisk = '/'.join([CONF.pxe.http_url, node.uuid, 'ramdisk'])
|
||||
else:
|
||||
deploy_kernel = pxe_info['deploy_kernel'][1]
|
||||
deploy_ramdisk = pxe_info['deploy_ramdisk'][1]
|
||||
kernel = pxe_info['kernel'][1]
|
||||
ramdisk = pxe_info['ramdisk'][1]
|
||||
if not is_whole_disk_image:
|
||||
kernel = pxe_info['kernel'][1]
|
||||
ramdisk = pxe_info['ramdisk'][1]
|
||||
|
||||
pxe_options = {
|
||||
'deployment_aki_path': deploy_kernel,
|
||||
'deployment_ari_path': deploy_ramdisk,
|
||||
'aki_path': kernel,
|
||||
'ari_path': ramdisk,
|
||||
'pxe_append_params': CONF.pxe.pxe_append_params,
|
||||
'tftp_server': CONF.pxe.tftp_server
|
||||
}
|
||||
|
||||
if not is_whole_disk_image:
|
||||
pxe_options.update({'aki_path': kernel,
|
||||
'ari_path': ramdisk})
|
||||
|
||||
deploy_ramdisk_options = iscsi_deploy.build_deploy_ramdisk_options(node)
|
||||
pxe_options.update(deploy_ramdisk_options)
|
||||
|
||||
@ -240,7 +246,7 @@ def _cache_ramdisk_kernel(ctx, node, pxe_info):
|
||||
"""Fetch the necessary kernels and ramdisks for the instance."""
|
||||
fileutils.ensure_tree(
|
||||
os.path.join(pxe_utils.get_root_dir(), node.uuid))
|
||||
LOG.debug("Fetching kernel and ramdisk for node %s",
|
||||
LOG.debug("Fetching necessary kernel and ramdisk for node %s",
|
||||
node.uuid)
|
||||
deploy_utils.fetch_images(ctx, TFTPImageCache(), pxe_info.values(),
|
||||
CONF.force_raw_images)
|
||||
@ -261,6 +267,9 @@ def _get_image_info(node, ctx):
|
||||
|
||||
image_info.update(pxe_utils.get_deploy_kr_info(node.uuid, d_info))
|
||||
|
||||
if node.driver_internal_info['is_whole_disk_image']:
|
||||
return image_info
|
||||
|
||||
i_info = node.instance_info
|
||||
labels = ('kernel', 'ramdisk')
|
||||
if not (i_info.get('kernel') and i_info.get('ramdisk')):
|
||||
@ -349,10 +358,13 @@ class PXEDeploy(base.DeployInterface):
|
||||
|
||||
iscsi_deploy.validate(task)
|
||||
|
||||
if service_utils.is_glance_image(d_info['image_source']):
|
||||
if node.driver_internal_info.get('is_whole_disk_image'):
|
||||
props = []
|
||||
elif service_utils.is_glance_image(d_info['image_source']):
|
||||
props = ['kernel_id', 'ramdisk_id']
|
||||
else:
|
||||
props = ['kernel', 'ramdisk']
|
||||
|
||||
iscsi_deploy.validate_image_properties(task.context, d_info, props)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
@ -428,25 +440,34 @@ class PXEDeploy(base.DeployInterface):
|
||||
# the image kernel and ramdisk (Or even require it).
|
||||
_cache_ramdisk_kernel(task.context, task.node, pxe_info)
|
||||
|
||||
iwdi = task.node.driver_internal_info['is_whole_disk_image']
|
||||
# NOTE(deva): prepare may be called from conductor._do_takeover
|
||||
# in which case, it may need to regenerate the PXE config file for an
|
||||
# already-active deployment.
|
||||
if task.node.provision_state == states.ACTIVE:
|
||||
# this should have been stashed when the deploy was done
|
||||
# but let's guard, just in case it's missing
|
||||
try:
|
||||
# this should have been stashed when the deploy was done
|
||||
# but let's guard, just in case it's missing
|
||||
root_uuid = task.node.driver_internal_info['root_uuid']
|
||||
root_uuid_or_disk_id = task.node.driver_internal_info[
|
||||
'root_uuid_or_disk_id']
|
||||
except KeyError:
|
||||
LOG.warn(_LW("The UUID for the root partition can't be found, "
|
||||
"unable to switch the pxe config from deployment "
|
||||
"mode to service (boot) mode for node %(node)s"),
|
||||
{"node": task.node.uuid})
|
||||
if not iwdi:
|
||||
LOG.warn(_LW("The UUID for the root partition can't be "
|
||||
"found, unable to switch the pxe config from "
|
||||
"deployment mode to service (boot) mode for node "
|
||||
"%(node)s"), {"node": task.node.uuid})
|
||||
else:
|
||||
LOG.warn(_LW("The disk id for the whole disk image can't "
|
||||
"be found, unable to switch the pxe config from "
|
||||
"deployment mode to service (boot) mode for "
|
||||
"node %(node)s"), {"node": task.node.uuid})
|
||||
else:
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
deploy_utils.switch_pxe_config(
|
||||
pxe_config_path, root_uuid,
|
||||
driver_utils.get_node_capability(task.node, 'boot_mode'))
|
||||
pxe_config_path, root_uuid_or_disk_id,
|
||||
driver_utils.get_node_capability(task.node, 'boot_mode'),
|
||||
iwdi)
|
||||
|
||||
def clean_up(self, task):
|
||||
"""Clean up the deployment environment for the task's node.
|
||||
@ -529,10 +550,9 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
task.process_event('resume')
|
||||
|
||||
_destroy_token_file(node)
|
||||
|
||||
root_uuid = iscsi_deploy.continue_deploy(task, **kwargs)
|
||||
|
||||
if not root_uuid:
|
||||
is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
|
||||
root_uuid_or_disk_id = iscsi_deploy.continue_deploy(task, **kwargs)
|
||||
if not root_uuid_or_disk_id:
|
||||
return
|
||||
|
||||
# save the node's root disk UUID so that another conductor could
|
||||
@ -540,7 +560,7 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
# we have to assign to node.driver_internal_info so the node knows it
|
||||
# has changed.
|
||||
driver_internal_info = node.driver_internal_info
|
||||
driver_internal_info['root_uuid'] = root_uuid
|
||||
driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
|
||||
@ -552,8 +572,10 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
pxe_utils.clean_up_pxe_config(task)
|
||||
else:
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
|
||||
deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
|
||||
driver_utils.get_node_capability(node, 'boot_mode'))
|
||||
node_cap = driver_utils.get_node_capability(node, 'boot_mode')
|
||||
deploy_utils.switch_pxe_config(pxe_config_path,
|
||||
root_uuid_or_disk_id,
|
||||
node_cap, is_whole_disk_image)
|
||||
|
||||
deploy_utils.notify_deploy_complete(kwargs['address'])
|
||||
LOG.info(_LI('Deployment to node %s done'), node.uuid)
|
||||
@ -588,11 +610,13 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
# it here.
|
||||
_destroy_token_file(node)
|
||||
|
||||
root_uuid = iscsi_deploy.do_agent_iscsi_deploy(task, self._client)
|
||||
|
||||
root_uuid_or_disk_id = iscsi_deploy.do_agent_iscsi_deploy(
|
||||
task,
|
||||
self._client)
|
||||
is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
|
||||
if iscsi_deploy.get_boot_option(node) == "local":
|
||||
# Install the boot loader
|
||||
self.configure_local_boot(task, root_uuid)
|
||||
self.configure_local_boot(task, root_uuid_or_disk_id)
|
||||
|
||||
# If it's going to boot from the local disk, get rid of
|
||||
# the PXE configuration files used for the deployment
|
||||
@ -600,7 +624,9 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
else:
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
|
||||
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
|
||||
deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
|
||||
boot_mode)
|
||||
deploy_utils.switch_pxe_config(
|
||||
pxe_config_path,
|
||||
root_uuid_or_disk_id,
|
||||
boot_mode, is_whole_disk_image)
|
||||
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
@ -6,6 +6,11 @@ append initrd={{ pxe_options.deployment_ari_path }} selinux=0 disk={{ pxe_option
|
||||
ipappend 3
|
||||
|
||||
|
||||
label boot
|
||||
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 }}
|
||||
|
@ -31,6 +31,7 @@ from oslo_utils import uuidutils
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common import images
|
||||
from ironic.common import keystone
|
||||
from ironic.common import states
|
||||
from ironic.common import swift
|
||||
@ -922,9 +923,11 @@ class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
|
||||
|
||||
@_mock_record_keepalive
|
||||
class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
@mock.patch.object(images, 'is_whole_disk_image')
|
||||
class ServiceDoNodeDeployTestCase(_ServiceSetUpMixin,
|
||||
tests_db_base.DbTestCase):
|
||||
def test_do_node_deploy_invalid_state(self):
|
||||
def test_do_node_deploy_invalid_state(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
# test that node deploy fails if the node is already provisioned
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
@ -939,8 +942,11 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
|
||||
|
||||
def test_do_node_deploy_maintenance(self):
|
||||
def test_do_node_deploy_maintenance(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
maintenance=True)
|
||||
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||
@ -952,8 +958,10 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
self.assertFalse(mock_iwdi.called)
|
||||
|
||||
def _test_do_node_deploy_validate_fail(self, mock_validate):
|
||||
def _test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
# InvalidParameterValue should be re-raised as InstanceDeployFailure
|
||||
mock_validate.side_effect = exception.InvalidParameterValue('error')
|
||||
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||
@ -966,15 +974,238 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.validate')
|
||||
def test_do_node_deploy_validate_fail(self, mock_validate):
|
||||
self._test_do_node_deploy_validate_fail(mock_validate)
|
||||
def test_do_node_deploy_validate_fail(self, mock_validate, mock_iwdi):
|
||||
self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakePower.validate')
|
||||
def test_do_node_deploy_power_validate_fail(self, mock_validate):
|
||||
self._test_do_node_deploy_validate_fail(mock_validate)
|
||||
def test_do_node_deploy_power_validate_fail(self, mock_validate,
|
||||
mock_iwdi):
|
||||
self._test_do_node_deploy_validate_fail(mock_validate, mock_iwdi)
|
||||
|
||||
@mock.patch('ironic.conductor.task_manager.TaskManager.process_event')
|
||||
def test_deploy_with_nostate_converts_to_available(self, mock_pe,
|
||||
mock_iwdi):
|
||||
# expressly create a node using the Juno-era NOSTATE state
|
||||
# and assert that it does not result in an error, and that the state
|
||||
# is converted to the new AVAILABLE state.
|
||||
# Mock the process_event call, because the transitions from
|
||||
# AVAILABLE are tested thoroughly elsewhere
|
||||
# NOTE(deva): This test can be deleted after Kilo is released
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.NOSTATE)
|
||||
self.assertEqual(states.NOSTATE, node.provision_state)
|
||||
self.service.do_node_deploy(self.context, node.uuid)
|
||||
self.assertTrue(mock_pe.called)
|
||||
node.refresh()
|
||||
self.assertEqual(states.AVAILABLE, node.provision_state)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
def test_do_node_deploy_partial_ok(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
thread = self.service._spawn_worker(lambda: None)
|
||||
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
|
||||
mock_spawn.return_value = thread
|
||||
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.AVAILABLE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYING, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# This is a sync operation last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_spawn.assert_called_once_with(mock.ANY, mock.ANY,
|
||||
mock.ANY, None)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state(self, mock_deploy, mock_iwdi):
|
||||
# This tests manager.do_node_deploy(), the 'else' path of
|
||||
# 'if new_state == states.DEPLOYDONE'. The node's states
|
||||
# aren't changed in this case.
|
||||
mock_iwdi.return_value = True
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYING
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE,
|
||||
instance_info={'image_source': uuidutils.generate_uuid(),
|
||||
'kernel': 'aaaa', 'ramdisk': 'bbbb'},
|
||||
driver_internal_info={'is_whole_disk_image': False})
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYING, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
# Verify instance_info values has been cleared.
|
||||
self.assertNotIn('kernel', node.instance_info)
|
||||
self.assertNotIn('ramdisk', node.instance_info)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
# Verify is_whole_disk_image reflects correct value on rebuild.
|
||||
self.assertTrue(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state_waiting(self, mock_deploy,
|
||||
mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYWAIT
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE,
|
||||
instance_info={'image_source': uuidutils.generate_uuid()})
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYWAIT, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state_done(self, mock_deploy,
|
||||
mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_deployfail_state(self, mock_deploy,
|
||||
mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.DEPLOYFAIL,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_error_state(self, mock_deploy, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ERROR,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
def test_do_node_deploy_rebuild_from_available_state(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
self._start_service()
|
||||
# test node will not rebuild if state is AVAILABLE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.AVAILABLE)
|
||||
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||
self.service.do_node_deploy,
|
||||
self.context, node['uuid'], rebuild=True)
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
|
||||
# Last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertNotIn('is_whole_disk_image', node.driver_internal_info)
|
||||
|
||||
def test_do_node_deploy_worker_pool_full(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
prv_state = states.AVAILABLE
|
||||
tgt_prv_state = states.NOSTATE
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
provision_state=prv_state,
|
||||
target_provision_state=tgt_prv_state,
|
||||
last_error=None, driver='fake')
|
||||
self._start_service()
|
||||
|
||||
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
|
||||
mock_spawn.side_effect = exception.NoFreeConductorWorker()
|
||||
|
||||
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||
self.service.do_node_deploy,
|
||||
self.context, node.uuid)
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
# Make sure things were rolled back
|
||||
self.assertEqual(prv_state, node.provision_state)
|
||||
self.assertEqual(tgt_prv_state, node.target_provision_state)
|
||||
self.assertIsNotNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
|
||||
|
||||
|
||||
@_mock_record_keepalive
|
||||
class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
tests_db_base.DbTestCase):
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare')
|
||||
def test__do_node_deploy_driver_raises_prepare_error(self, mock_prepare,
|
||||
@ -1105,163 +1336,6 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertIsNone(node.last_error)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch('ironic.conductor.task_manager.TaskManager.process_event')
|
||||
def test_deploy_with_nostate_converts_to_available(self, mock_pe):
|
||||
# expressly create a node using the Juno-era NOSTATE state
|
||||
# and assert that it does not result in an error, and that the state
|
||||
# is converted to the new AVAILABLE state.
|
||||
# Mock the process_event call, because the transitions from
|
||||
# AVAILABLE are tested thoroughly elsewhere
|
||||
# NOTE(deva): This test can be deleted after Kilo is released
|
||||
self._start_service()
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.NOSTATE)
|
||||
self.assertEqual(states.NOSTATE, node.provision_state)
|
||||
self.service.do_node_deploy(self.context, node.uuid)
|
||||
self.assertTrue(mock_pe.called)
|
||||
node.refresh()
|
||||
self.assertEqual(states.AVAILABLE, node.provision_state)
|
||||
|
||||
def test_do_node_deploy_partial_ok(self):
|
||||
self._start_service()
|
||||
thread = self.service._spawn_worker(lambda: None)
|
||||
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
|
||||
mock_spawn.return_value = thread
|
||||
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.AVAILABLE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYING, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# This is a sync operation last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_spawn.assert_called_once_with(mock.ANY, mock.ANY,
|
||||
mock.ANY, None)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state(self, mock_deploy):
|
||||
# This tests manager.do_node_deploy(), the 'else' path of
|
||||
# 'if new_state == states.DEPLOYDONE'. The node's states
|
||||
# aren't changed in this case.
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYING
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE,
|
||||
instance_info={'image_source': uuidutils.generate_uuid(),
|
||||
'kernel': 'aaaa', 'ramdisk': 'bbbb'})
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYING, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
# Verify instance_info values has been cleared.
|
||||
self.assertNotIn('kernel', node.instance_info)
|
||||
self.assertNotIn('ramdisk', node.instance_info)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state_waiting(self, mock_deploy):
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYWAIT
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE,
|
||||
instance_info={'image_source': uuidutils.generate_uuid()})
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.DEPLOYWAIT, node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_active_state_done(self, mock_deploy):
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ACTIVE,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_deployfail_state(self, mock_deploy):
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.DEPLOYFAIL,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test_do_node_deploy_rebuild_error_state(self, mock_deploy):
|
||||
self._start_service()
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.ERROR,
|
||||
target_provision_state=states.NOSTATE)
|
||||
|
||||
self.service.do_node_deploy(self.context, node.uuid, rebuild=True)
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
self.assertEqual(states.ACTIVE, node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, node.target_provision_state)
|
||||
# last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
mock_deploy.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_do_node_deploy_rebuild_from_available_state(self):
|
||||
self._start_service()
|
||||
# test node will not rebuild if state is AVAILABLE
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.AVAILABLE)
|
||||
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||
self.service.do_node_deploy,
|
||||
self.context, node['uuid'], rebuild=True)
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
|
||||
# Last_error should be None.
|
||||
self.assertIsNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.clean_up')
|
||||
def test__check_deploy_timeouts(self, mock_cleanup):
|
||||
self._start_service()
|
||||
@ -1279,32 +1353,6 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertIsNotNone(node.last_error)
|
||||
mock_cleanup.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_do_node_deploy_worker_pool_full(self):
|
||||
prv_state = states.AVAILABLE
|
||||
tgt_prv_state = states.NOSTATE
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
provision_state=prv_state,
|
||||
target_provision_state=tgt_prv_state,
|
||||
last_error=None, driver='fake')
|
||||
self._start_service()
|
||||
|
||||
with mock.patch.object(self.service, '_spawn_worker') as mock_spawn:
|
||||
mock_spawn.side_effect = exception.NoFreeConductorWorker()
|
||||
|
||||
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||
self.service.do_node_deploy,
|
||||
self.context, node.uuid)
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
# Make sure things were rolled back
|
||||
self.assertEqual(prv_state, node.provision_state)
|
||||
self.assertEqual(tgt_prv_state, node.target_provision_state)
|
||||
self.assertIsNotNone(node.last_error)
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
|
||||
def test_do_node_tear_down_invalid_state(self):
|
||||
self._start_service()
|
||||
# test node.provision_state is incorrect for tear_down
|
||||
@ -1332,10 +1380,11 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
|
||||
def test_do_node_tear_down_driver_raises_error(self, mock_tear_down):
|
||||
# test when driver.deploy.tear_down raises exception
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.DELETING,
|
||||
target_provision_state=states.AVAILABLE,
|
||||
instance_info={'foo': 'bar'})
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake', provision_state=states.DELETING,
|
||||
target_provision_state=states.AVAILABLE,
|
||||
instance_info={'foo': 'bar'},
|
||||
driver_internal_info={'is_whole_disk_image': False})
|
||||
|
||||
task = task_manager.TaskManager(self.context, node.uuid)
|
||||
self._start_service()
|
||||
@ -1354,10 +1403,11 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
|
||||
def test__do_node_tear_down_ok(self, mock_tear_down, mock_clean):
|
||||
# test when driver.deploy.tear_down succeeds
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=states.DELETING,
|
||||
target_provision_state=states.AVAILABLE,
|
||||
instance_info={'foo': 'bar'})
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake', provision_state=states.DELETING,
|
||||
target_provision_state=states.AVAILABLE,
|
||||
instance_info={'foo': 'bar'},
|
||||
driver_internal_info={'is_whole_disk_image': False})
|
||||
|
||||
task = task_manager.TaskManager(self.context, node.uuid)
|
||||
self._start_service()
|
||||
@ -1375,10 +1425,11 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.tear_down')
|
||||
def _test_do_node_tear_down_from_state(self, init_state, mock_tear_down,
|
||||
mock_clean):
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
provision_state=init_state,
|
||||
target_provision_state=states.AVAILABLE)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake', uuid=uuidutils.generate_uuid(),
|
||||
provision_state=init_state,
|
||||
target_provision_state=states.AVAILABLE,
|
||||
driver_internal_info={'is_whole_disk_image': False})
|
||||
|
||||
self.service.do_node_tear_down(self.context, node.uuid)
|
||||
self.service._worker_pool.waitall()
|
||||
@ -1410,11 +1461,12 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
prv_state = states.ACTIVE
|
||||
tgt_prv_state = states.NOSTATE
|
||||
fake_instance_info = {'foo': 'bar'}
|
||||
node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
provision_state=prv_state,
|
||||
target_provision_state=tgt_prv_state,
|
||||
instance_info=fake_instance_info,
|
||||
last_error=None)
|
||||
driver_internal_info = {'is_whole_disk_image': False}
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake', provision_state=prv_state,
|
||||
target_provision_state=tgt_prv_state,
|
||||
instance_info=fake_instance_info,
|
||||
driver_internal_info=driver_internal_info, last_error=None)
|
||||
self._start_service()
|
||||
|
||||
mock_spawn.side_effect = exception.NoFreeConductorWorker()
|
||||
@ -1426,8 +1478,9 @@ class DoNodeDeployTearDownTestCase(_ServiceSetUpMixin,
|
||||
self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
|
||||
self.service._worker_pool.waitall()
|
||||
node.refresh()
|
||||
# Assert instance_info was not touched
|
||||
# Assert instance_info/driver_internal_info was not touched
|
||||
self.assertEqual(fake_instance_info, node.instance_info)
|
||||
self.assertEqual(driver_internal_info, node.driver_internal_info)
|
||||
# Make sure things were rolled back
|
||||
self.assertEqual(prv_state, node.provision_state)
|
||||
self.assertEqual(tgt_prv_state, node.target_provision_state)
|
||||
@ -1511,9 +1564,12 @@ class MiscTestCase(_ServiceSetUpMixin, _CommonMixIn, tests_db_base.DbTestCase):
|
||||
self.assertTrue(self.service._mapped_to_this_conductor(n['uuid'],
|
||||
'fake'))
|
||||
self.assertFalse(self.service._mapped_to_this_conductor(n['uuid'],
|
||||
|
||||
'otherdriver'))
|
||||
|
||||
def test_validate_driver_interfaces(self):
|
||||
@mock.patch.object(images, 'is_whole_disk_image')
|
||||
def test_validate_driver_interfaces(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||
ret = self.service.validate_driver_interfaces(self.context,
|
||||
node.uuid)
|
||||
@ -1523,8 +1579,11 @@ class MiscTestCase(_ServiceSetUpMixin, _CommonMixIn, tests_db_base.DbTestCase):
|
||||
'management': {'result': True},
|
||||
'deploy': {'result': True}}
|
||||
self.assertEqual(expected, ret)
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
|
||||
def test_validate_driver_interfaces_validation_fail(self):
|
||||
@mock.patch.object(images, 'is_whole_disk_image')
|
||||
def test_validate_driver_interfaces_validation_fail(self, mock_iwdi):
|
||||
mock_iwdi.return_value = False
|
||||
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||
with mock.patch(
|
||||
'ironic.drivers.modules.fake.FakeDeploy.validate'
|
||||
@ -1535,6 +1594,7 @@ class MiscTestCase(_ServiceSetUpMixin, _CommonMixIn, tests_db_base.DbTestCase):
|
||||
node.uuid)
|
||||
self.assertFalse(ret['deploy']['result'])
|
||||
self.assertEqual(reason, ret['deploy']['reason'])
|
||||
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
|
||||
|
||||
@mock.patch.object(manager.ConductorManager, '_mapped_to_this_conductor')
|
||||
@mock.patch.object(dbapi.IMPL, 'get_nodeinfo_list')
|
||||
|
@ -69,6 +69,12 @@ def get_test_pxe_driver_info():
|
||||
}
|
||||
|
||||
|
||||
def get_test_pxe_driver_internal_info():
|
||||
return {
|
||||
"is_whole_disk_image": False,
|
||||
}
|
||||
|
||||
|
||||
def get_test_pxe_instance_info():
|
||||
return {
|
||||
"image_source": "glance://image_uuid",
|
||||
|
@ -6,6 +6,11 @@ append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/deploy_ramdisk seli
|
||||
ipappend 3
|
||||
|
||||
|
||||
label boot
|
||||
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 }}
|
||||
|
@ -27,6 +27,7 @@ from oslo_concurrency import processutils
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import requests
|
||||
import testtools
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import disk_partitioner
|
||||
@ -51,22 +52,47 @@ kernel deploy_kernel
|
||||
append initrd=deploy_ramdisk
|
||||
ipappend 3
|
||||
|
||||
label boot
|
||||
label boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root={{ ROOT }}
|
||||
|
||||
label boot_whole_disk
|
||||
COM32 chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
"""
|
||||
|
||||
_PXECONF_BOOT = """
|
||||
default boot
|
||||
_PXECONF_BOOT_PARTITION = """
|
||||
default boot_partition
|
||||
|
||||
label deploy
|
||||
kernel deploy_kernel
|
||||
append initrd=deploy_ramdisk
|
||||
ipappend 3
|
||||
|
||||
label boot
|
||||
label boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root=UUID=12345678-1234-1234-1234-1234567890abcdef
|
||||
|
||||
label boot_whole_disk
|
||||
COM32 chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
"""
|
||||
|
||||
_PXECONF_BOOT_WHOLE_DISK = """
|
||||
default boot_whole_disk
|
||||
|
||||
label deploy
|
||||
kernel deploy_kernel
|
||||
append initrd=deploy_ramdisk
|
||||
ipappend 3
|
||||
|
||||
label boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root={{ ROOT }}
|
||||
|
||||
label boot_whole_disk
|
||||
COM32 chain.c32
|
||||
append mbr:0x12345678
|
||||
"""
|
||||
|
||||
_IPXECONF_DEPLOY = """
|
||||
@ -81,28 +107,61 @@ kernel deploy_kernel
|
||||
initrd deploy_ramdisk
|
||||
boot
|
||||
|
||||
:boot
|
||||
:boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root={{ ROOT }}
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
kernel chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
boot
|
||||
"""
|
||||
|
||||
_IPXECONF_BOOT = """
|
||||
_IPXECONF_BOOT_PARTITION = """
|
||||
#!ipxe
|
||||
|
||||
dhcp
|
||||
|
||||
goto boot
|
||||
goto boot_partition
|
||||
|
||||
:deploy
|
||||
kernel deploy_kernel
|
||||
initrd deploy_ramdisk
|
||||
boot
|
||||
|
||||
:boot
|
||||
:boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root=UUID=12345678-1234-1234-1234-1234567890abcdef
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
kernel chain.c32
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
boot
|
||||
"""
|
||||
|
||||
_IPXECONF_BOOT_WHOLE_DISK = """
|
||||
#!ipxe
|
||||
|
||||
dhcp
|
||||
|
||||
goto boot_whole_disk
|
||||
|
||||
:deploy
|
||||
kernel deploy_kernel
|
||||
initrd deploy_ramdisk
|
||||
boot
|
||||
|
||||
:boot_partition
|
||||
kernel kernel
|
||||
append initrd=ramdisk root={{ ROOT }}
|
||||
boot
|
||||
|
||||
:boot_whole_disk
|
||||
kernel chain.c32
|
||||
append mbr:0x12345678
|
||||
boot
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_DEPLOY = """
|
||||
@ -114,13 +173,17 @@ image=deploy_kernel
|
||||
append="ro text"
|
||||
|
||||
image=kernel
|
||||
label=boot
|
||||
label=boot_partition
|
||||
initrd=ramdisk
|
||||
append="root={{ ROOT }}"
|
||||
|
||||
image=chain.c32
|
||||
label=boot_whole_disk
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_BOOT = """
|
||||
default=boot
|
||||
_UEFI_PXECONF_BOOT_PARTITION = """
|
||||
default=boot_partition
|
||||
|
||||
image=deploy_kernel
|
||||
label=deploy
|
||||
@ -128,9 +191,31 @@ image=deploy_kernel
|
||||
append="ro text"
|
||||
|
||||
image=kernel
|
||||
label=boot
|
||||
label=boot_partition
|
||||
initrd=ramdisk
|
||||
append="root=UUID=12345678-1234-1234-1234-1234567890abcdef"
|
||||
|
||||
image=chain.c32
|
||||
label=boot_whole_disk
|
||||
append mbr:{{ DISK_IDENTIFIER }}
|
||||
"""
|
||||
|
||||
_UEFI_PXECONF_BOOT_WHOLE_DISK = """
|
||||
default=boot_whole_disk
|
||||
|
||||
image=deploy_kernel
|
||||
label=deploy
|
||||
initrd=deploy_ramdisk
|
||||
append="ro text"
|
||||
|
||||
image=kernel
|
||||
label=boot_partition
|
||||
initrd=ramdisk
|
||||
append="root={{ ROOT }}"
|
||||
|
||||
image=chain.c32
|
||||
label=boot_whole_disk
|
||||
append mbr:0x12345678
|
||||
"""
|
||||
|
||||
|
||||
@ -148,7 +233,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.attach_mock(mocker, name)
|
||||
return parent_mock
|
||||
|
||||
def _test_deploy(self, boot_option=None):
|
||||
def _test_deploy_partition_image(self, boot_option=None):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -179,10 +264,10 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.make_partitions.return_value = {'root': root_part,
|
||||
'swap': swap_part}
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
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:
|
||||
@ -207,24 +292,26 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
if boot_option:
|
||||
kwargs = {'boot_option': boot_option}
|
||||
|
||||
returned_root_uuid = utils.deploy(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, **kwargs)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
|
||||
def test_deploy_without_boot_option(self):
|
||||
self._test_deploy()
|
||||
def test_deploy_partition_image_without_boot_option(self):
|
||||
self._test_deploy_partition_image()
|
||||
|
||||
def test_deploy_netboot(self):
|
||||
self._test_deploy(boot_option="netboot")
|
||||
def test_deploy_partition_image_netboot(self):
|
||||
self._test_deploy_partition_image(boot_option="netboot")
|
||||
|
||||
def test_deploy_localboot(self):
|
||||
self._test_deploy(boot_option="local")
|
||||
def test_deploy_partition_image_localboot(self):
|
||||
self._test_deploy_partition_image(boot_option="local")
|
||||
|
||||
def test_deploy_without_swap(self):
|
||||
def test_deploy_partition_image_without_swap(self):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -253,10 +340,10 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.block_uuid.return_value = root_uuid
|
||||
parent_mock.make_partitions.return_value = {'root': root_part}
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
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,
|
||||
@ -269,15 +356,17 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
returned_root_uuid = utils.deploy(address, port, iqn, lun,
|
||||
image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format,
|
||||
node_uuid)
|
||||
returned_root_uuid = utils.deploy_partition_image(address, port, iqn,
|
||||
lun, image_path,
|
||||
root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format,
|
||||
node_uuid)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
|
||||
def test_deploy_with_ephemeral(self):
|
||||
def test_deploy_partition_image_with_ephemeral(self):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -311,10 +400,10 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
'ephemeral': ephemeral_part,
|
||||
'root': root_part}
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
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,
|
||||
@ -332,15 +421,17 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
returned_root_uuid = utils.deploy(address, port, iqn, lun,
|
||||
image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format,
|
||||
node_uuid)
|
||||
returned_root_uuid = utils.deploy_partition_image(address, port, iqn,
|
||||
lun, image_path,
|
||||
root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format,
|
||||
node_uuid)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
|
||||
def test_deploy_preserve_ephemeral(self):
|
||||
def test_deploy_partition_image_preserve_ephemeral(self):
|
||||
"""Check if all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -375,10 +466,10 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
'root': root_part}
|
||||
parent_mock.block_uuid.return_value = root_uuid
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
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.make_partitions(dev, root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
configdrive_mb,
|
||||
@ -393,18 +484,21 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
returned_root_uuid = utils.deploy(address, port, iqn, lun,
|
||||
image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format,
|
||||
node_uuid, preserve_ephemeral=True,
|
||||
boot_option="netboot")
|
||||
returned_root_uuid = utils.deploy_partition_image(address, port, iqn,
|
||||
lun, image_path,
|
||||
root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format,
|
||||
node_uuid,
|
||||
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)
|
||||
|
||||
@mock.patch.object(common_utils, 'unlink_without_raise')
|
||||
def test_deploy_with_configdrive(self, mock_unlink):
|
||||
def test_deploy_partition_image_with_configdrive(self, mock_unlink):
|
||||
"""Check loosely all functions are called with right args."""
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
@ -439,10 +533,10 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
configdrive_part}
|
||||
parent_mock._get_configdrive.return_value = (10, 'configdrive-path')
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
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._get_configdrive(configdrive_url,
|
||||
node_uuid),
|
||||
@ -459,16 +553,49 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
returned_root_uuid = utils.deploy(address, port, iqn, lun,
|
||||
image_path, root_mb, swap_mb,
|
||||
ephemeral_mb, ephemeral_format,
|
||||
node_uuid,
|
||||
configdrive=configdrive_url)
|
||||
returned_root_uuid = utils.deploy_partition_image(address, port, iqn,
|
||||
lun, image_path,
|
||||
root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format,
|
||||
node_uuid,
|
||||
configdrive=configdrive_url)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
self.assertEqual(root_uuid, returned_root_uuid)
|
||||
mock_unlink.assert_called_once_with('configdrive-path')
|
||||
|
||||
@mock.patch.object(utils, 'get_disk_identifier')
|
||||
def test_deploy_whole_disk_image(self, mock_gdi):
|
||||
"""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'
|
||||
node_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
|
||||
|
||||
dev = '/dev/fake'
|
||||
name_list = ['get_dev', 'discovery', 'login_iscsi', 'logout_iscsi',
|
||||
'delete_iscsi', 'is_block_device', 'populate_image',
|
||||
'notify']
|
||||
parent_mock = self._mock_calls(name_list)
|
||||
parent_mock.get_dev.return_value = dev
|
||||
parent_mock.is_block_device.return_value = True
|
||||
mock_gdi.return_value = '0x12345678'
|
||||
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.populate_image(image_path, dev),
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
utils.deploy_disk_image(address, port, iqn, lun,
|
||||
image_path, node_uuid)
|
||||
|
||||
self.assertEqual(calls_expected, parent_mock.mock_calls)
|
||||
|
||||
@mock.patch.object(common_utils, 'execute')
|
||||
def test_verify_iscsi_connection_raises(self, mock_exec):
|
||||
iqn = 'iqn.xyz'
|
||||
@ -552,6 +679,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
|
||||
mock_check_dev.assert_called_once_with(address, port, iqn)
|
||||
|
||||
@mock.patch.object(utils, 'is_block_device', lambda d: True)
|
||||
def test_always_logout_and_delete_iscsi(self):
|
||||
"""Check if logout_iscsi() and delete_iscsi() are called.
|
||||
|
||||
@ -590,9 +718,9 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
parent_mock.get_image_mb.return_value = 1
|
||||
parent_mock.work_on_disk.side_effect = TestException
|
||||
calls_expected = [mock.call.get_dev(address, port, iqn, lun),
|
||||
mock.call.get_image_mb(image_path),
|
||||
mock.call.discovery(address, port),
|
||||
mock.call.login_iscsi(address, port, iqn),
|
||||
mock.call.get_image_mb(image_path),
|
||||
mock.call.work_on_disk(dev, root_mb, swap_mb,
|
||||
ephemeral_mb,
|
||||
ephemeral_format, image_path,
|
||||
@ -602,7 +730,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
|
||||
mock.call.logout_iscsi(address, port, iqn),
|
||||
mock.call.delete_iscsi(address, port, iqn)]
|
||||
|
||||
self.assertRaises(TestException, utils.deploy,
|
||||
self.assertRaises(TestException, utils.deploy_partition_image,
|
||||
address, port, iqn, lun, image_path,
|
||||
root_mb, swap_mb, ephemeral_mb, ephemeral_format,
|
||||
node_uuid)
|
||||
@ -623,36 +751,73 @@ class SwitchPxeConfigTestCase(tests_base.TestCase):
|
||||
self.addCleanup(os.unlink, fname)
|
||||
return fname
|
||||
|
||||
def test_switch_pxe_config(self):
|
||||
def test_switch_pxe_config_partition_image(self):
|
||||
boot_mode = 'bios'
|
||||
fname = self._create_config()
|
||||
utils.switch_pxe_config(fname,
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode)
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode,
|
||||
False)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_PXECONF_BOOT, pxeconf)
|
||||
self.assertEqual(_PXECONF_BOOT_PARTITION, pxeconf)
|
||||
|
||||
def test_switch_ipxe_config(self):
|
||||
def test_switch_pxe_config_whole_disk_image(self):
|
||||
boot_mode = 'bios'
|
||||
fname = self._create_config()
|
||||
utils.switch_pxe_config(fname,
|
||||
'0x12345678',
|
||||
boot_mode,
|
||||
True)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_PXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
def test_switch_ipxe_config_partition_image(self):
|
||||
boot_mode = 'bios'
|
||||
cfg.CONF.set_override('ipxe_enabled', True, 'pxe')
|
||||
fname = self._create_config(ipxe=True)
|
||||
utils.switch_pxe_config(fname,
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode)
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode,
|
||||
False)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_IPXECONF_BOOT, pxeconf)
|
||||
self.assertEqual(_IPXECONF_BOOT_PARTITION, pxeconf)
|
||||
|
||||
def test_switch_uefi_pxe_config(self):
|
||||
def test_switch_ipxe_config_whole_disk_image(self):
|
||||
boot_mode = 'bios'
|
||||
cfg.CONF.set_override('ipxe_enabled', True, 'pxe')
|
||||
fname = self._create_config(ipxe=True)
|
||||
utils.switch_pxe_config(fname,
|
||||
'0x12345678',
|
||||
boot_mode,
|
||||
True)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_IPXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
def test_switch_uefi_pxe_config_partition_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode)
|
||||
utils.switch_pxe_config(fname,
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode)
|
||||
'12345678-1234-1234-1234-1234567890abcdef',
|
||||
boot_mode,
|
||||
False)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT, pxeconf)
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_PARTITION, pxeconf)
|
||||
|
||||
def test_switch_uefi_config_whole_disk_image(self):
|
||||
boot_mode = 'uefi'
|
||||
fname = self._create_config(boot_mode=boot_mode)
|
||||
utils.switch_pxe_config(fname,
|
||||
'0x12345678',
|
||||
boot_mode,
|
||||
True)
|
||||
with open(fname, 'r') as f:
|
||||
pxeconf = f.read()
|
||||
self.assertEqual(_UEFI_PXECONF_BOOT_WHOLE_DISK, pxeconf)
|
||||
|
||||
|
||||
@mock.patch('time.sleep', lambda sec: None)
|
||||
@ -727,34 +892,21 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.return_value = {'swap': self.swap_part,
|
||||
'root': self.root_part}
|
||||
|
||||
def test_no_parent_device(self):
|
||||
def test_no_root_partition(self):
|
||||
self.mock_ibd.return_value = False
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
utils.work_on_disk, self.dev, self.root_mb,
|
||||
self.swap_mb, self.ephemeral_mb,
|
||||
self.ephemeral_format, self.image_path, 'fake-uuid')
|
||||
self.mock_ibd.assert_called_once_with(self.dev)
|
||||
self.assertFalse(self.mock_mp.called,
|
||||
"make_partitions mock was unexpectedly called.")
|
||||
|
||||
def test_no_root_partition(self):
|
||||
self.mock_ibd.side_effect = [True, False]
|
||||
calls = [mock.call(self.dev),
|
||||
mock.call(self.root_part)]
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
utils.work_on_disk, self.dev, self.root_mb,
|
||||
self.swap_mb, self.ephemeral_mb,
|
||||
self.ephemeral_format, self.image_path, 'fake-uuid')
|
||||
self.assertEqual(self.mock_ibd.call_args_list, calls)
|
||||
self.mock_ibd.assert_called_once_with(self.root_part)
|
||||
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")
|
||||
|
||||
def test_no_swap_partition(self):
|
||||
self.mock_ibd.side_effect = [True, True, False]
|
||||
calls = [mock.call(self.dev),
|
||||
mock.call(self.root_part),
|
||||
self.mock_ibd.side_effect = [True, False]
|
||||
calls = [mock.call(self.root_part),
|
||||
mock.call(self.swap_part)]
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
utils.work_on_disk, self.dev, self.root_mb,
|
||||
@ -776,9 +928,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.return_value = {'ephemeral': ephemeral_part,
|
||||
'swap': swap_part,
|
||||
'root': root_part}
|
||||
self.mock_ibd.side_effect = [True, True, True, False]
|
||||
calls = [mock.call(self.dev),
|
||||
mock.call(root_part),
|
||||
self.mock_ibd.side_effect = [True, True, False]
|
||||
calls = [mock.call(root_part),
|
||||
mock.call(swap_part),
|
||||
mock.call(ephemeral_part)]
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
@ -804,9 +955,8 @@ class WorkOnDiskTestCase(tests_base.TestCase):
|
||||
self.mock_mp.return_value = {'swap': swap_part,
|
||||
'configdrive': configdrive_part,
|
||||
'root': root_part}
|
||||
self.mock_ibd.side_effect = [True, True, True, False]
|
||||
calls = [mock.call(self.dev),
|
||||
mock.call(root_part),
|
||||
self.mock_ibd.side_effect = [True, True, False]
|
||||
calls = [mock.call(root_part),
|
||||
mock.call(swap_part),
|
||||
mock.call(configdrive_part)]
|
||||
self.assertRaises(exception.InstanceDeployFailure,
|
||||
@ -1217,3 +1367,41 @@ class TrySetBootDeviceTestCase(db_base.DbTestCase):
|
||||
task, boot_devices.DISK, persistent=True)
|
||||
node_set_boot_device_mock.assert_called_once_with(
|
||||
task, boot_devices.DISK, persistent=True)
|
||||
|
||||
|
||||
@mock.patch.object(utils, 'is_block_device')
|
||||
@mock.patch.object(utils, 'login_iscsi', lambda *_: None)
|
||||
@mock.patch.object(utils, 'discovery', lambda *_: None)
|
||||
@mock.patch.object(utils, 'logout_iscsi', lambda *_: None)
|
||||
@mock.patch.object(utils, 'delete_iscsi', lambda *_: None)
|
||||
@mock.patch.object(utils, 'get_dev', lambda *_: '/dev/fake')
|
||||
class ISCSISetupAndHandleErrorsTestCase(tests_base.TestCase):
|
||||
|
||||
def test_no_parent_device(self, mock_ibd):
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
iqn = 'iqn.xyz'
|
||||
lun = 1
|
||||
image_path = '/tmp/xyz/image'
|
||||
mock_ibd.return_value = False
|
||||
expected_dev = '/dev/fake'
|
||||
with testtools.ExpectedException(exception.InstanceDeployFailure):
|
||||
with utils._iscsi_setup_and_handle_errors(
|
||||
address, port, iqn, lun, image_path) as dev:
|
||||
self.assertEqual(expected_dev, dev)
|
||||
|
||||
mock_ibd.assert_called_once_with(expected_dev)
|
||||
|
||||
def test_parent_device_yield(self, mock_ibd):
|
||||
address = '127.0.0.1'
|
||||
port = 3306
|
||||
iqn = 'iqn.xyz'
|
||||
lun = 1
|
||||
image_path = '/tmp/xyz/image'
|
||||
expected_dev = '/dev/fake'
|
||||
mock_ibd.return_value = True
|
||||
with utils._iscsi_setup_and_handle_errors(address, port,
|
||||
iqn, lun, image_path) as dev:
|
||||
self.assertEqual(expected_dev, dev)
|
||||
|
||||
mock_ibd.assert_called_once_with(expected_dev)
|
||||
|
@ -42,15 +42,18 @@ CONF = cfg.CONF
|
||||
|
||||
INST_INFO_DICT = db_utils.get_test_pxe_instance_info()
|
||||
DRV_INFO_DICT = db_utils.get_test_pxe_driver_info()
|
||||
DRV_INTERNAL_INFO_DICT = db_utils.get_test_pxe_driver_internal_info()
|
||||
|
||||
|
||||
class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
|
||||
def test_parse_instance_info_good(self):
|
||||
# make sure we get back the expected things
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT
|
||||
)
|
||||
info = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertIsNotNone(info.get('image_source'))
|
||||
self.assertIsNotNone(info.get('root_gb'))
|
||||
@ -61,7 +64,10 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
# make sure error is raised when info is missing
|
||||
info = dict(INST_INFO_DICT)
|
||||
del info['image_source']
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
node)
|
||||
@ -70,7 +76,11 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
# make sure error is raised when info is missing
|
||||
info = dict(INST_INFO_DICT)
|
||||
del info['root_gb']
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
node)
|
||||
@ -78,7 +88,10 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
def test_parse_instance_info_invalid_root_gb(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['root_gb'] = 'foobar'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
node)
|
||||
@ -89,7 +102,10 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['ephemeral_gb'] = ephemeral_gb
|
||||
info['ephemeral_format'] = ephemeral_fmt
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertEqual(ephemeral_gb, data.get('ephemeral_gb'))
|
||||
self.assertEqual(ephemeral_fmt, data.get('ephemeral_format'))
|
||||
@ -98,7 +114,11 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['ephemeral_gb'] = 'foobar'
|
||||
info['ephemeral_format'] = 'exttest'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
node)
|
||||
@ -110,7 +130,10 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
info['ephemeral_gb'] = ephemeral_gb
|
||||
info['ephemeral_format'] = None
|
||||
self.config(default_ephemeral_format=ephemeral_fmt, group='pxe')
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
instance_info = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertEqual(ephemeral_fmt, instance_info['ephemeral_format'])
|
||||
|
||||
@ -119,9 +142,12 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
for opt in ['true', 'TRUE', 'True', 't',
|
||||
'on', 'yes', 'y', '1']:
|
||||
info['preserve_ephemeral'] = opt
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
instance_info=info)
|
||||
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, uuid=uuidutils.generate_uuid(),
|
||||
instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertTrue(data.get('preserve_ephemeral'))
|
||||
|
||||
@ -130,16 +156,21 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
for opt in ['false', 'FALSE', 'False', 'f',
|
||||
'off', 'no', 'n', '0']:
|
||||
info['preserve_ephemeral'] = opt
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, uuid=uuidutils.generate_uuid(),
|
||||
instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
data = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertFalse(data.get('preserve_ephemeral'))
|
||||
|
||||
def test_parse_instance_info_invalid_preserve_ephemeral(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['preserve_ephemeral'] = 'foobar'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.parse_instance_info,
|
||||
node)
|
||||
@ -147,7 +178,10 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
def test_parse_instance_info_configdrive(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
info['configdrive'] = 'http://1.2.3.4/cd'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
instance_info = iscsi_deploy.parse_instance_info(node)
|
||||
self.assertEqual('http://1.2.3.4/cd', instance_info['configdrive'])
|
||||
|
||||
@ -156,23 +190,31 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
info['image_source'] = 'file:///image.qcow2'
|
||||
info['kernel'] = 'file:///image.vmlinuz'
|
||||
info['ramdisk'] = 'file:///image.initrd'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
iscsi_deploy.parse_instance_info(node)
|
||||
|
||||
def test_parse_instance_info_nonglance_image_no_kernel(self):
|
||||
info = INST_INFO_DICT.copy()
|
||||
info['image_source'] = 'file:///image.qcow2'
|
||||
info['ramdisk'] = 'file:///image.initrd'
|
||||
node = obj_utils.create_test_node(self.context, instance_info=info)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=info,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
iscsi_deploy.parse_instance_info, node)
|
||||
|
||||
@mock.patch.object(image_service, 'get_image_service')
|
||||
def test_validate_image_properties_glance_image(self, image_service_mock):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
d_info = pxe._parse_deploy_info(node)
|
||||
image_service_mock.return_value.show.return_value = {
|
||||
'properties': {'kernel_id': '1111', 'ramdisk_id': '2222'},
|
||||
@ -187,10 +229,12 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(image_service, 'get_image_service')
|
||||
def test_validate_image_properties_glance_image_missing_prop(self,
|
||||
image_service_mock):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
d_info = pxe._parse_deploy_info(node)
|
||||
image_service_mock.return_value.show.return_value = {
|
||||
'properties': {'kernel_id': '1111'},
|
||||
@ -239,10 +283,12 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
'root_gb': 100,
|
||||
}
|
||||
image_service_show_mock.return_value = {'size': 1, 'properties': {}}
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
d_info = pxe._parse_deploy_info(node)
|
||||
iscsi_deploy.validate_image_properties(self.context, d_info,
|
||||
['kernel', 'ramdisk'])
|
||||
@ -260,15 +306,38 @@ class IscsiDeployValidateParametersTestCase(db_base.DbTestCase):
|
||||
}
|
||||
img_service_show_mock.side_effect = exception.ImageRefValidationFailed(
|
||||
image_href='http://ubuntu', reason='HTTPError')
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
d_info = pxe._parse_deploy_info(node)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
iscsi_deploy.validate_image_properties, self.context,
|
||||
d_info, ['kernel', 'ramdisk'])
|
||||
|
||||
def test_parse_instance_info_whole_disk_image(self):
|
||||
driver_internal_info = dict(DRV_INTERNAL_INFO_DICT)
|
||||
driver_internal_info['is_whole_disk_image'] = True
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, instance_info=INST_INFO_DICT,
|
||||
driver_internal_info=driver_internal_info,
|
||||
)
|
||||
instance_info = iscsi_deploy.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'))
|
||||
self.assertEqual(0, instance_info.get('ephemeral_gb'))
|
||||
self.assertIsNone(instance_info.get('configdrive'))
|
||||
|
||||
def test_parse_instance_info_whole_disk_image_missing_root(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
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)
|
||||
|
||||
|
||||
class IscsiDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
|
||||
@ -278,6 +347,7 @@ class IscsiDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
'driver': 'fake_pxe',
|
||||
'instance_info': INST_INFO_DICT,
|
||||
'driver_info': DRV_INFO_DICT,
|
||||
'driver_internal_info': DRV_INTERNAL_INFO_DICT,
|
||||
}
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
self.node = obj_utils.create_test_node(self.context, **n)
|
||||
@ -304,6 +374,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
'driver': 'fake_pxe',
|
||||
'instance_info': instance_info,
|
||||
'driver_info': DRV_INFO_DICT,
|
||||
'driver_internal_info': DRV_INTERNAL_INFO_DICT,
|
||||
}
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
self.node = obj_utils.create_test_node(self.context, **n)
|
||||
@ -440,7 +511,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
@mock.patch.object(deploy_utils, 'deploy')
|
||||
@mock.patch.object(deploy_utils, 'deploy_partition_image')
|
||||
def test_continue_deploy_fail(self, deploy_mock, power_mock,
|
||||
mock_image_cache):
|
||||
kwargs = {'address': '123456', 'iqn': 'aaa-bbb', 'key': 'fake-56789'}
|
||||
@ -464,7 +535,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
@mock.patch.object(deploy_utils, 'deploy')
|
||||
@mock.patch.object(deploy_utils, 'deploy_partition_image')
|
||||
def test_continue_deploy_ramdisk_fails(self, deploy_mock, power_mock,
|
||||
mock_image_cache):
|
||||
kwargs = {'address': '123456', 'iqn': 'aaa-bbb', 'key': 'fake-56789',
|
||||
@ -488,7 +559,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(iscsi_deploy, 'get_deploy_info')
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
@mock.patch.object(deploy_utils, 'deploy')
|
||||
@mock.patch.object(deploy_utils, 'deploy_partition_image')
|
||||
def test_continue_deploy(self, deploy_mock, power_mock, mock_image_cache,
|
||||
mock_deploy_info, mock_log):
|
||||
kwargs = {'address': '123456', 'iqn': 'aaa-bbb', 'key': 'fake-56789'}
|
||||
@ -531,6 +602,47 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
mock_image_cache.assert_called_once_with()
|
||||
mock_image_cache.return_value.clean_up.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'LOG')
|
||||
@mock.patch.object(iscsi_deploy, 'get_deploy_info')
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
@mock.patch.object(deploy_utils, 'deploy_disk_image')
|
||||
def test_continue_deploy_whole_disk_image(
|
||||
self, deploy_mock, power_mock, mock_image_cache, mock_deploy_info,
|
||||
mock_log):
|
||||
kwargs = {'address': '123456', 'iqn': 'aaa-bbb', 'key': 'fake-56789'}
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
|
||||
mock_deploy_info.return_value = {
|
||||
'address': '123456',
|
||||
'image_path': (u'/var/lib/ironic/images/1be26c0b-03f2-4d2e-ae87-'
|
||||
u'c02d7f33c123/disk'),
|
||||
'iqn': 'aaa-bbb',
|
||||
'lun': '1',
|
||||
'node_uuid': u'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
||||
'port': '3260',
|
||||
'root_mb': 102400,
|
||||
}
|
||||
log_params = mock_deploy_info.return_value.copy()
|
||||
expected_dict = {
|
||||
'node': self.node.uuid,
|
||||
'params': log_params,
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
mock_log.isEnabledFor.return_value = True
|
||||
iscsi_deploy.continue_deploy(task, **kwargs)
|
||||
mock_log.debug.assert_called_once_with(
|
||||
mock.ANY, expected_dict)
|
||||
self.assertEqual(states.DEPLOYWAIT, task.node.provision_state)
|
||||
self.assertEqual(states.ACTIVE, task.node.target_provision_state)
|
||||
self.assertIsNone(task.node.last_error)
|
||||
mock_image_cache.assert_called_once_with()
|
||||
mock_image_cache.return_value.clean_up.assert_called_once_with()
|
||||
|
||||
def test_get_deploy_info_boot_option_default(self):
|
||||
instance_info = self.node.instance_info
|
||||
instance_info['deploy_key'] = 'key'
|
||||
@ -588,8 +700,9 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
|
||||
task, error=None, iqn='iqn-qweqwe', key='abcdef',
|
||||
address='1.2.3.4')
|
||||
self.assertEqual('some-root-uuid', ret_val)
|
||||
self.assertEqual('some-root-uuid',
|
||||
task.node.driver_internal_info['root_uuid'])
|
||||
self.assertEqual(
|
||||
'some-root-uuid',
|
||||
task.node.driver_internal_info['root_uuid_or_disk_id'])
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options')
|
||||
def test_do_agent_iscsi_deploy_start_iscsi_failure(self,
|
||||
|
@ -50,16 +50,21 @@ CONF = cfg.CONF
|
||||
|
||||
INST_INFO_DICT = db_utils.get_test_pxe_instance_info()
|
||||
DRV_INFO_DICT = db_utils.get_test_pxe_driver_info()
|
||||
DRV_INTERNAL_INFO_DICT = db_utils.get_test_pxe_driver_internal_info()
|
||||
|
||||
|
||||
class PXEValidateParametersTestCase(db_base.DbTestCase):
|
||||
|
||||
def test__parse_deploy_info(self):
|
||||
# make sure we get back the expected things
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
|
||||
info = pxe._parse_deploy_info(node)
|
||||
self.assertIsNotNone(info.get('deploy_ramdisk'))
|
||||
self.assertIsNotNone(info.get('deploy_kernel'))
|
||||
@ -114,6 +119,7 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
'driver': 'fake_pxe',
|
||||
'instance_info': INST_INFO_DICT,
|
||||
'driver_info': DRV_INFO_DICT,
|
||||
'driver_internal_info': DRV_INTERNAL_INFO_DICT,
|
||||
}
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
self.node = obj_utils.create_test_node(self.context, **n)
|
||||
@ -159,6 +165,25 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
self.assertEqual('instance_ramdisk_uuid',
|
||||
self.node.instance_info.get('ramdisk'))
|
||||
|
||||
@mock.patch.object(base_image_service.BaseImageService, '_show')
|
||||
def test__get_image_info_whole_disk_image(self, show_mock):
|
||||
properties = {'properties': None}
|
||||
|
||||
expected_info = {'deploy_ramdisk':
|
||||
(DRV_INFO_DICT['deploy_ramdisk'],
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_ramdisk')),
|
||||
'deploy_kernel':
|
||||
(DRV_INFO_DICT['deploy_kernel'],
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_kernel'))}
|
||||
show_mock.return_value = properties
|
||||
self.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
image_info = pxe._get_image_info(self.node, self.context)
|
||||
self.assertEqual(expected_info, image_info)
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options')
|
||||
@mock.patch.object(pxe_utils, '_build_pxe_config')
|
||||
def _test_build_pxe_config_options(self, build_pxe_mock, deploy_opts_mock,
|
||||
@ -231,9 +256,7 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
'ramdisk': ('ramdisk_id',
|
||||
os.path.join(root_dir,
|
||||
self.node.uuid,
|
||||
'ramdisk'))
|
||||
}
|
||||
|
||||
'ramdisk'))}
|
||||
options = pxe._build_pxe_config_options(self.node,
|
||||
image_info,
|
||||
self.context)
|
||||
@ -245,6 +268,66 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
def test__build_pxe_config_options_ipxe(self):
|
||||
self._test_build_pxe_config_options(ipxe_enabled=True)
|
||||
|
||||
@mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options')
|
||||
@mock.patch.object(pxe_utils, '_build_pxe_config')
|
||||
def _test_build_pxe_config_options_whole_disk_image(self, build_pxe_mock,
|
||||
deploy_opts_mock, ipxe_enabled=False):
|
||||
self.config(pxe_append_params='test_param', group='pxe')
|
||||
# NOTE: right '/' should be removed from url string
|
||||
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',
|
||||
'deployment_id': 'fake-deploy-id',
|
||||
'deployment_key': 'fake-deploy-key',
|
||||
'disk': 'fake-disk',
|
||||
'ironic_api_url': 'fake-api-url'}
|
||||
|
||||
deploy_opts_mock.return_value = fake_deploy_opts
|
||||
|
||||
tftp_server = CONF.pxe.tftp_server
|
||||
|
||||
if ipxe_enabled:
|
||||
http_url = 'http://192.1.2.3:1234'
|
||||
self.config(ipxe_enabled=True, group='pxe')
|
||||
self.config(http_url=http_url, group='pxe')
|
||||
|
||||
deploy_kernel = os.path.join(http_url, self.node.uuid,
|
||||
'deploy_kernel')
|
||||
deploy_ramdisk = os.path.join(http_url, self.node.uuid,
|
||||
'deploy_ramdisk')
|
||||
root_dir = CONF.pxe.http_root
|
||||
else:
|
||||
deploy_kernel = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
|
||||
'deploy_kernel')
|
||||
deploy_ramdisk = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
|
||||
'deploy_ramdisk')
|
||||
root_dir = CONF.pxe.tftp_root
|
||||
|
||||
expected_options = {
|
||||
'deployment_ari_path': deploy_ramdisk,
|
||||
'pxe_append_params': 'test_param',
|
||||
'deployment_aki_path': deploy_kernel,
|
||||
'tftp_server': tftp_server,
|
||||
}
|
||||
|
||||
expected_options.update(fake_deploy_opts)
|
||||
|
||||
image_info = {'deploy_kernel': ('deploy_kernel',
|
||||
os.path.join(root_dir,
|
||||
self.node.uuid,
|
||||
'deploy_kernel')),
|
||||
'deploy_ramdisk': ('deploy_ramdisk',
|
||||
os.path.join(root_dir,
|
||||
self.node.uuid,
|
||||
'deploy_ramdisk')),
|
||||
}
|
||||
self.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
options = pxe._build_pxe_config_options(self.node,
|
||||
image_info,
|
||||
self.context)
|
||||
self.assertEqual(expected_options, options)
|
||||
|
||||
def test_get_token_file_path(self):
|
||||
node_uuid = self.node.uuid
|
||||
self.assertEqual('/tftpboot/token-' + node_uuid,
|
||||
@ -311,10 +394,12 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
instance_info = INST_INFO_DICT
|
||||
instance_info['deploy_key'] = 'fake-56789'
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver='fake_pxe',
|
||||
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/')
|
||||
@ -338,6 +423,13 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
|
||||
@mock.patch.object(base_image_service.BaseImageService, '_show')
|
||||
def test_validate_good_whole_disk_image(self, mock_glance):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.deploy.validate(task)
|
||||
|
||||
def test_validate_fail(self):
|
||||
info = dict(INST_INFO_DICT)
|
||||
del info['image_source']
|
||||
@ -570,7 +662,8 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_cap.return_value = None
|
||||
|
||||
self.node.provision_state = states.ACTIVE
|
||||
self.node.driver_internal_info = {'root_uuid': 'abcd'}
|
||||
self.node.driver_internal_info = {'root_uuid_or_disk_id': 'abcd',
|
||||
'is_whole_disk_image': False}
|
||||
self.node.save()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
@ -583,7 +676,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
task.node, None)
|
||||
|
||||
mock_pxe_get_cfg.assert_called_once_with(task.node.uuid)
|
||||
mock_switch.assert_called_once_with('/path', 'abcd', None)
|
||||
mock_switch.assert_called_once_with('/path', 'abcd', None, False)
|
||||
|
||||
@mock.patch.object(keystone, 'token_expires_soon')
|
||||
@mock.patch.object(deploy_utils, 'get_image_mb')
|
||||
@ -712,7 +805,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
@mock.patch.object(deploy_utils, 'notify_deploy_complete')
|
||||
@mock.patch.object(deploy_utils, 'switch_pxe_config')
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(deploy_utils, 'deploy')
|
||||
@mock.patch.object(deploy_utils, 'deploy_partition_image')
|
||||
def _test_continue_deploy(self, is_localboot, mock_deploy,
|
||||
mock_image_cache, mock_switch_config,
|
||||
notify_mock, mock_node_boot_dev, mock_clean_pxe):
|
||||
@ -732,6 +825,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
root_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
|
||||
mock_deploy.return_value = root_uuid
|
||||
boot_mode = None
|
||||
is_whole_disk_image = False
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.vendor._continue_deploy(
|
||||
@ -741,7 +835,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
self.assertEqual(states.ACTIVE, self.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, self.node.target_provision_state)
|
||||
self.assertEqual(states.POWER_ON, self.node.power_state)
|
||||
self.assertIn('root_uuid', self.node.driver_internal_info)
|
||||
self.assertIn('root_uuid_or_disk_id', self.node.driver_internal_info)
|
||||
self.assertIsNone(self.node.last_error)
|
||||
self.assertFalse(os.path.exists(token_path))
|
||||
mock_image_cache.assert_called_once_with()
|
||||
@ -754,8 +848,70 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
mock_clean_pxe.assert_called_once_with(mock.ANY)
|
||||
self.assertFalse(mock_switch_config.called)
|
||||
else:
|
||||
mock_switch_config.assert_called_once_with(
|
||||
pxe_config_path, root_uuid, boot_mode)
|
||||
mock_switch_config.assert_called_once_with(pxe_config_path,
|
||||
root_uuid,
|
||||
boot_mode,
|
||||
is_whole_disk_image)
|
||||
self.assertFalse(mock_node_boot_dev.called)
|
||||
self.assertFalse(mock_clean_pxe.called)
|
||||
|
||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_config')
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device')
|
||||
@mock.patch.object(deploy_utils, 'notify_deploy_complete')
|
||||
@mock.patch.object(deploy_utils, 'switch_pxe_config')
|
||||
@mock.patch.object(iscsi_deploy, 'InstanceImageCache')
|
||||
@mock.patch.object(deploy_utils, 'deploy_disk_image')
|
||||
def _test_continue_deploy_whole_disk_image(self, is_localboot,
|
||||
mock_deploy,
|
||||
mock_image_cache,
|
||||
mock_switch_config,
|
||||
notify_mock,
|
||||
mock_node_boot_dev,
|
||||
mock_clean_pxe):
|
||||
token_path = self._create_token_file()
|
||||
|
||||
# set local boot
|
||||
if is_localboot:
|
||||
i_info = self.node.instance_info
|
||||
i_info['capabilities'] = '{"boot_option": "local"}'
|
||||
self.node.instance_info = i_info
|
||||
|
||||
self.node.power_state = states.POWER_ON
|
||||
self.node.provision_state = states.DEPLOYWAIT
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
|
||||
boot_mode = None
|
||||
is_whole_disk_image = True
|
||||
disk_id = '0x12345678'
|
||||
mock_deploy.return_value = disk_id
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.vendor._continue_deploy(task, address='123456',
|
||||
iqn='aaa-bbb',
|
||||
key='fake-56789')
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual(states.ACTIVE, self.node.provision_state)
|
||||
self.assertEqual(states.NOSTATE, self.node.target_provision_state)
|
||||
self.assertEqual(states.POWER_ON, self.node.power_state)
|
||||
self.assertIsNone(self.node.last_error)
|
||||
self.assertFalse(os.path.exists(token_path))
|
||||
mock_image_cache.assert_called_once_with()
|
||||
mock_image_cache.return_value.clean_up.assert_called_once_with()
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
|
||||
notify_mock.assert_called_once_with('123456')
|
||||
if is_localboot:
|
||||
mock_node_boot_dev.assert_called_once_with(
|
||||
mock.ANY, boot_devices.DISK, persistent=True)
|
||||
mock_clean_pxe.assert_called_once_with(mock.ANY)
|
||||
self.assertFalse(mock_switch_config.called)
|
||||
else:
|
||||
mock_switch_config.assert_called_once_with(pxe_config_path,
|
||||
disk_id,
|
||||
boot_mode,
|
||||
is_whole_disk_image)
|
||||
self.assertFalse(mock_node_boot_dev.called)
|
||||
self.assertFalse(mock_clean_pxe.called)
|
||||
|
||||
@ -765,6 +921,12 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
def test_continue_deploy_localboot(self):
|
||||
self._test_continue_deploy(True)
|
||||
|
||||
def test_continue_deploy_whole_disk_image(self):
|
||||
self._test_continue_deploy_whole_disk_image(False)
|
||||
|
||||
def test_continue_deploy_whole_disk_image_localboot(self):
|
||||
self._test_continue_deploy_whole_disk_image(True)
|
||||
|
||||
def test_continue_deploy_invalid(self):
|
||||
self.node.power_state = states.POWER_ON
|
||||
self.node.provision_state = states.AVAILABLE
|
||||
@ -821,10 +983,12 @@ class CleanUpTestCase(db_base.DbTestCase):
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
instance_info = INST_INFO_DICT
|
||||
instance_info['deploy_key'] = 'fake-56789'
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
|
||||
def test_clean_up(self, mock_image_info, mock_cache, mock_pxe_clean,
|
||||
mock_iscsi_clean, mock_unlink):
|
||||
@ -866,10 +1030,12 @@ class CleanUpFullFlowTestCase(db_base.DbTestCase):
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
instance_info = INST_INFO_DICT
|
||||
instance_info['deploy_key'] = 'fake-56789'
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_pxe',
|
||||
instance_info=instance_info,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='fake_pxe',
|
||||
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)
|
||||
|
||||
@ -945,9 +1111,12 @@ class TestAgentVendorPassthru(db_base.DbTestCase):
|
||||
mgr_utils.mock_the_extension_manager()
|
||||
self.driver = driver_factory.get_driver("fake")
|
||||
self.driver.vendor = pxe.VendorPassthru()
|
||||
self.node = obj_utils.create_test_node(self.context, driver='fake',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT)
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='fake',
|
||||
instance_info=INST_INFO_DICT,
|
||||
driver_info=DRV_INFO_DICT,
|
||||
driver_internal_info=DRV_INTERNAL_INFO_DICT,
|
||||
)
|
||||
self.node.driver_internal_info['agent_url'] = 'http://1.2.3.4:1234'
|
||||
self.task = mock.Mock(spec=task_manager.TaskManager)
|
||||
self.task.shared = False
|
||||
@ -966,7 +1135,6 @@ class TestAgentVendorPassthru(db_base.DbTestCase):
|
||||
reboot_and_finish_deploy_mock):
|
||||
|
||||
do_agent_iscsi_deploy_mock.return_value = 'some-root-uuid'
|
||||
|
||||
self.driver.vendor.continue_deploy(self.task)
|
||||
destroy_token_file_mock.assert_called_once_with(self.node)
|
||||
do_agent_iscsi_deploy_mock.assert_called_once_with(
|
||||
@ -974,7 +1142,7 @@ class TestAgentVendorPassthru(db_base.DbTestCase):
|
||||
tftp_config = '/tftpboot/%s/config' % self.node.uuid
|
||||
switch_pxe_config_mock.assert_called_once_with(tftp_config,
|
||||
'some-root-uuid',
|
||||
None)
|
||||
None, False)
|
||||
reboot_and_finish_deploy_mock.assert_called_once_with(self.task)
|
||||
|
||||
@mock.patch.object(agent_base_vendor.BaseAgentVendor,
|
||||
|
@ -25,6 +25,7 @@ from oslo_config import cfg
|
||||
import six.moves.builtins as __builtin__
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils as glance_utils
|
||||
from ironic.common import image_service
|
||||
from ironic.common import images
|
||||
from ironic.common import utils
|
||||
@ -214,6 +215,68 @@ class IronicImagesTestCase(base.TestCase):
|
||||
qemu_img_info_mock.assert_called_once_with('path')
|
||||
self.assertEqual(1, size)
|
||||
|
||||
@mock.patch.object(images, 'get_image_properties')
|
||||
@mock.patch.object(glance_utils, 'is_glance_image')
|
||||
def test_is_whole_disk_image_no_img_src(self, mock_igi, mock_gip):
|
||||
instance_info = {'image_source': ''}
|
||||
iwdi = images.is_whole_disk_image('context', instance_info)
|
||||
self.assertIsNone(iwdi)
|
||||
self.assertFalse(mock_igi.called)
|
||||
self.assertFalse(mock_gip.called)
|
||||
|
||||
@mock.patch.object(images, 'get_image_properties')
|
||||
@mock.patch.object(glance_utils, 'is_glance_image')
|
||||
def test_is_whole_disk_image_partition_image(self, mock_igi, mock_gip):
|
||||
mock_igi.return_value = True
|
||||
mock_gip.return_value = {'kernel_id': 'kernel',
|
||||
'ramdisk_id': 'ramdisk'}
|
||||
instance_info = {'image_source': 'glance://partition_image'}
|
||||
image_source = instance_info['image_source']
|
||||
is_whole_disk_image = images.is_whole_disk_image('context',
|
||||
instance_info)
|
||||
self.assertFalse(is_whole_disk_image)
|
||||
mock_igi.assert_called_once_with(image_source)
|
||||
mock_gip.assert_called_once_with('context', image_source)
|
||||
|
||||
@mock.patch.object(images, 'get_image_properties')
|
||||
@mock.patch.object(glance_utils, 'is_glance_image')
|
||||
def test_is_whole_disk_image_whole_disk_image(self, mock_igi, mock_gip):
|
||||
mock_igi.return_value = True
|
||||
mock_gip.return_value = {}
|
||||
instance_info = {'image_source': 'glance://whole_disk_image'}
|
||||
image_source = instance_info['image_source']
|
||||
is_whole_disk_image = images.is_whole_disk_image('context',
|
||||
instance_info)
|
||||
self.assertTrue(is_whole_disk_image)
|
||||
mock_igi.assert_called_once_with(image_source)
|
||||
mock_gip.assert_called_once_with('context', image_source)
|
||||
|
||||
@mock.patch.object(images, 'get_image_properties')
|
||||
@mock.patch.object(glance_utils, 'is_glance_image')
|
||||
def test_is_whole_disk_image_partition_non_glance(self, mock_igi,
|
||||
mock_gip):
|
||||
mock_igi.return_value = False
|
||||
instance_info = {'image_source': 'partition_image',
|
||||
'kernel': 'kernel',
|
||||
'ramdisk': 'ramdisk'}
|
||||
is_whole_disk_image = images.is_whole_disk_image('context',
|
||||
instance_info)
|
||||
self.assertFalse(is_whole_disk_image)
|
||||
self.assertFalse(mock_gip.called)
|
||||
mock_igi.assert_called_once_with(instance_info['image_source'])
|
||||
|
||||
@mock.patch.object(images, 'get_image_properties')
|
||||
@mock.patch.object(glance_utils, 'is_glance_image')
|
||||
def test_is_whole_disk_image_whole_disk_non_glance(self, mock_igi,
|
||||
mock_gip):
|
||||
mock_igi.return_value = False
|
||||
instance_info = {'image_source': 'whole_disk_image'}
|
||||
is_whole_disk_image = images.is_whole_disk_image('context',
|
||||
instance_info)
|
||||
self.assertTrue(is_whole_disk_image)
|
||||
self.assertFalse(mock_gip.called)
|
||||
mock_igi.assert_called_once_with(instance_info['image_source'])
|
||||
|
||||
|
||||
class FsImageTestCase(base.TestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user