summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFaizan Barmawer <faizan.barmawer@gmail.com>2015-05-20 01:16:37 -0700
committerFaizan Barmawer <faizan.barmawer@gmail.com>2015-12-09 22:03:35 -0800
commit7c138f01a0085e49aab240eb2f7fc63601c348ff (patch)
tree5694cefda3466e793fb77e77bfe48dcff8066505
parent7b13f3b0a6dce57de61d23a5130835570da6658b (diff)
downloadironic-7c138f01a0085e49aab240eb2f7fc63601c348ff.tar.gz
Refactor disk partitioner code from ironic and use ironic-lib.
The disk partitioner related code from ironic/common/disk_partitioner.py and ironic/drivers/modules/deploy_utils.py is being moved to ironic-lib. The code in ironic needs to be removed and use ironic-lib to perform disk related activities. Change-Id: I7b1b1d8b45b947a7b5715f3c6ab49d84e84b6b90
-rw-r--r--etc/ironic/ironic.conf.sample45
-rw-r--r--etc/ironic/rootwrap.d/ironic-lib.filters19
-rw-r--r--etc/ironic/rootwrap.d/ironic-utils.filters7
-rw-r--r--ironic/common/disk_partitioner.py226
-rw-r--r--ironic/drivers/modules/deploy_utils.py478
-rw-r--r--ironic/drivers/modules/iscsi_deploy.py3
-rw-r--r--ironic/tests/unit/common/test_disk_partitioner.py198
-rw-r--r--ironic/tests/unit/drivers/modules/test_deploy_utils.py1053
-rw-r--r--ironic/tests/unit/drivers/modules/test_iscsi_deploy.py5
-rw-r--r--releasenotes/notes/refactor-ironic-lib-22939896d8d46a77.yaml20
-rw-r--r--requirements.txt1
-rw-r--r--tools/config/oslo.config.generator.rc2
12 files changed, 423 insertions, 1634 deletions
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index f55f67e6a..4c7a3abbb 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -908,18 +908,6 @@
# Options defined in ironic.drivers.modules.deploy_utils
#
-# Size of EFI system partition in MiB when configuring UEFI
-# systems for local boot. (integer value)
-#efi_system_partition_size=200
-
-# Block size to use when writing to the nodes disk. (string
-# value)
-#dd_block_size=1M
-
-# Maximum attempts to verify an iSCSI connection is active,
-# sleeping 1 second between attempts. (integer value)
-#iscsi_verify_attempts=3
-
# ironic-conductor node's HTTP server URL. Example:
# http://192.1.2.3:8080 (string value)
# Deprecated group/name - [pxe]/http_url
@@ -956,7 +944,7 @@
[disk_partitioner]
#
-# Options defined in ironic.common.disk_partitioner
+# Options defined in ironic_lib.disk_partitioner
#
# After Ironic has completed creating the partition table, it
@@ -972,6 +960,25 @@
#check_device_max_retries=20
+[disk_utils]
+
+#
+# Options defined in ironic_lib.disk_utils
+#
+
+# Size of EFI system partition in MiB when configuring UEFI
+# systems for local boot. (integer value)
+#efi_system_partition_size=200
+
+# Block size to use when writing to the nodes disk. (string
+# value)
+#dd_block_size=1M
+
+# Maximum attempts to verify an iSCSI connection is active,
+# sleeping 1 second between attempts. (integer value)
+#iscsi_verify_attempts=3
+
+
[drac]
#
@@ -1281,6 +1288,18 @@
#sensor_method=ipmitool
+[ironic_lib]
+
+#
+# Options defined in ironic_lib.utils
+#
+
+# Command that is prefixed to commands that are run as root.
+# If not specified, no commands are run as root. (string
+# value)
+#root_helper=sudo ironic-rootwrap /etc/ironic/rootwrap.conf
+
+
[keystone]
#
diff --git a/etc/ironic/rootwrap.d/ironic-lib.filters b/etc/ironic/rootwrap.d/ironic-lib.filters
new file mode 100644
index 000000000..acce7a7b6
--- /dev/null
+++ b/etc/ironic/rootwrap.d/ironic-lib.filters
@@ -0,0 +1,19 @@
+# An ironic-lib.filters to be used with rootwrap command.
+# The following commands should be used in filters for disk manipulation.
+# This file should be owned by (and only-writeable by) the root user.
+
+[Filters]
+# ironic_lib/disk_utils.py
+blkid: CommandFilter, blkid, root
+blockdev: CommandFilter, blockdev, root
+hexdump: CommandFilter, hexdump, root
+qemu-img: CommandFilter, qemu-img, root
+
+# ironic_lib/utils.py
+mkswap: CommandFilter, mkswap, root
+mkfs: CommandFilter, mkfs, root
+dd: CommandFilter, dd, root
+
+# ironic_lib/disk_partitioner.py
+fuser: CommandFilter, fuser, root
+parted: CommandFilter, parted, root
diff --git a/etc/ironic/rootwrap.d/ironic-utils.filters b/etc/ironic/rootwrap.d/ironic-utils.filters
index 972ddf5cf..1c7eb858e 100644
--- a/etc/ironic/rootwrap.d/ironic-utils.filters
+++ b/etc/ironic/rootwrap.d/ironic-utils.filters
@@ -4,9 +4,6 @@
[Filters]
# ironic/drivers/modules/deploy_utils.py
iscsiadm: CommandFilter, iscsiadm, root
-blkid: CommandFilter, blkid, root
-blockdev: CommandFilter, blockdev, root
-hexdump: CommandFilter, hexdump, root
# ironic/common/utils.py
mkswap: CommandFilter, mkswap, root
@@ -14,7 +11,3 @@ mkfs: CommandFilter, mkfs, root
mount: CommandFilter, mount, root
umount: CommandFilter, umount, root
dd: CommandFilter, dd, root
-
-# ironic/common/disk_partitioner.py
-fuser: CommandFilter, fuser, root
-parted: CommandFilter, parted, root
diff --git a/ironic/common/disk_partitioner.py b/ironic/common/disk_partitioner.py
deleted file mode 100644
index f80c8c257..000000000
--- a/ironic/common/disk_partitioner.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright 2014 Red Hat, Inc.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import re
-
-from oslo_concurrency import processutils
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_service import loopingcall
-
-from ironic.common import exception
-from ironic.common.i18n import _
-from ironic.common.i18n import _LW
-from ironic.common import utils
-
-opts = [
- cfg.IntOpt('check_device_interval',
- default=1,
- help=_('After Ironic has completed creating the partition '
- 'table, it continues to check for activity on the '
- 'attached iSCSI device status at this interval prior '
- 'to copying the image to the node, in seconds')),
- cfg.IntOpt('check_device_max_retries',
- default=20,
- help=_('The maximum number of times to check that the device '
- 'is not accessed by another process. If the device is '
- 'still busy after that, the disk partitioning will be '
- 'treated as having failed.')),
-]
-
-CONF = cfg.CONF
-opt_group = cfg.OptGroup(name='disk_partitioner',
- title='Options for the disk partitioner')
-CONF.register_group(opt_group)
-CONF.register_opts(opts, opt_group)
-
-LOG = logging.getLogger(__name__)
-
-
-class DiskPartitioner(object):
-
- def __init__(self, device, disk_label='msdos', alignment='optimal'):
- """A convenient wrapper around the parted tool.
-
- :param device: The device path.
- :param disk_label: The type of the partition table. Valid types are:
- "bsd", "dvh", "gpt", "loop", "mac", "msdos",
- "pc98", or "sun".
- :param alignment: Set alignment for newly created partitions.
- Valid types are: none, cylinder, minimal and
- optimal.
-
- """
- self._device = device
- self._disk_label = disk_label
- self._alignment = alignment
- self._partitions = []
- self._fuser_pids_re = re.compile(r'((\d)+\s*)+')
-
- def _exec(self, *args):
- # NOTE(lucasagomes): utils.execute() is already a wrapper on top
- # of processutils.execute() which raises specific
- # exceptions. It also logs any failure so we don't
- # need to log it again here.
- utils.execute('parted', '-a', self._alignment, '-s', self._device,
- '--', 'unit', 'MiB', *args, check_exit_code=[0],
- use_standard_locale=True, run_as_root=True)
-
- def add_partition(self, size, part_type='primary', fs_type='',
- bootable=False):
- """Add a partition.
-
- :param size: The size of the partition in MiB.
- :param part_type: The type of the partition. Valid values are:
- primary, logical, or extended.
- :param fs_type: The filesystem type. Valid types are: ext2, fat32,
- fat16, HFS, linux-swap, NTFS, reiserfs, ufs.
- If blank (''), it will create a Linux native
- partition (83).
- :param bootable: Boolean value; whether the partition is bootable
- or not.
- :returns: The partition number.
-
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please
- # also do the same modification in ironic-lib
- self._partitions.append({'size': size,
- 'type': part_type,
- 'fs_type': fs_type,
- 'bootable': bootable})
- return len(self._partitions)
-
- def get_partitions(self):
- """Get the partitioning layout.
-
- :returns: An iterator with the partition number and the
- partition layout.
-
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please
- # also do the same modification in ironic-lib
- return enumerate(self._partitions, 1)
-
- def _wait_for_disk_to_become_available(self, retries, max_retries, pids,
- stderr):
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please
- # also do the same modification in ironic-lib
- retries[0] += 1
- if retries[0] > max_retries:
- raise loopingcall.LoopingCallDone()
-
- try:
- # NOTE(ifarkas): fuser returns a non-zero return code if none of
- # the specified files is accessed
- out, err = utils.execute('fuser', self._device,
- check_exit_code=[0, 1], run_as_root=True)
-
- if not out and not err:
- raise loopingcall.LoopingCallDone()
- else:
- if err:
- stderr[0] = err
- if out:
- pids_match = re.search(self._fuser_pids_re, out)
- pids[0] = pids_match.group()
- except processutils.ProcessExecutionError as exc:
- LOG.warning(_LW('Failed to check the device %(device)s with fuser:'
- ' %(err)s'), {'device': self._device, 'err': exc})
-
- def commit(self):
- """Write to the disk."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please
- # also do the same modification in ironic-lib
- LOG.debug("Committing partitions to disk.")
- cmd_args = ['mklabel', self._disk_label]
- # NOTE(lucasagomes): Lead in with 1MiB to allow room for the
- # partition table itself.
- start = 1
- for num, part in self.get_partitions():
- end = start + part['size']
- cmd_args.extend(['mkpart', part['type'], part['fs_type'],
- str(start), str(end)])
- if part['bootable']:
- cmd_args.extend(['set', str(num), 'boot', 'on'])
- start = end
-
- self._exec(*cmd_args)
-
- retries = [0]
- pids = ['']
- fuser_err = ['']
- interval = CONF.disk_partitioner.check_device_interval
- max_retries = CONF.disk_partitioner.check_device_max_retries
-
- timer = loopingcall.FixedIntervalLoopingCall(
- self._wait_for_disk_to_become_available,
- retries, max_retries, pids, fuser_err)
- timer.start(interval=interval).wait()
-
- if retries[0] > max_retries:
- if pids[0]:
- raise exception.InstanceDeployFailure(
- _('Disk partitioning failed on device %(device)s. '
- 'Processes with the following PIDs are holding it: '
- '%(pids)s. Time out waiting for completion.')
- % {'device': self._device, 'pids': pids[0]})
- else:
- raise exception.InstanceDeployFailure(
- _('Disk partitioning failed on device %(device)s. Fuser '
- 'exited with "%(fuser_err)s". Time out waiting for '
- 'completion.')
- % {'device': self._device, 'fuser_err': fuser_err[0]})
-
-
-_PARTED_PRINT_RE = re.compile(r"^(\d+):([\d\.]+)MiB:"
- "([\d\.]+)MiB:([\d\.]+)MiB:(\w*)::(\w*)")
-
-
-def list_partitions(device):
- """Get partitions information from given device.
-
- :param device: The device path.
- :returns: list of dictionaries (one per partition) with keys:
- number, start, end, size (in MiB), filesystem, flags
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- output = utils.execute(
- 'parted', '-s', '-m', device, 'unit', 'MiB', 'print',
- use_standard_locale=True, run_as_root=True)[0]
- if isinstance(output, bytes):
- output = output.decode("utf-8")
- lines = [line for line in output.split('\n') if line.strip()][2:]
- # Example of line: 1:1.00MiB:501MiB:500MiB:ext4::boot
- fields = ('number', 'start', 'end', 'size', 'filesystem', 'flags')
- result = []
- for line in lines:
- match = _PARTED_PRINT_RE.match(line)
- if match is None:
- LOG.warning(_LW("Partition information from parted for device "
- "%(device)s does not match "
- "expected format: %(line)s"),
- dict(device=device, line=line))
- continue
- # Cast int fields to ints (some are floats and we round them down)
- groups = [int(float(x)) if i < 4 else x
- for i, x in enumerate(match.groups())]
- result.append(dict(zip(fields, groups)))
- return result
diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py
index 432095979..3b9d93b30 100644
--- a/ironic/drivers/modules/deploy_utils.py
+++ b/ironic/drivers/modules/deploy_utils.py
@@ -14,38 +14,28 @@
# under the License.
-import base64
import contextlib
-import gzip
-import math
import os
import re
-import shutil
import socket
-import stat
-import tempfile
import time
+from ironic_lib import disk_utils
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import excutils
-from oslo_utils import units
-import requests
import six
from six.moves.urllib import parse
from ironic.common import dhcp_factory
-from ironic.common import disk_partitioner
from ironic.common import exception
from ironic.common.glance_service import service_utils
from ironic.common.i18n import _
from ironic.common.i18n import _LE
-from ironic.common.i18n import _LI
from ironic.common.i18n import _LW
from ironic.common import image_service
-from ironic.common import images
from ironic.common import keystone
from ironic.common import states
from ironic.common import utils
@@ -57,17 +47,6 @@ from ironic import objects
deploy_opts = [
- cfg.IntOpt('efi_system_partition_size',
- default=200,
- help=_('Size of EFI system partition in MiB when configuring '
- 'UEFI systems for local boot.')),
- cfg.StrOpt('dd_block_size',
- default='1M',
- help=_('Block size to use when writing to the nodes disk.')),
- cfg.IntOpt('iscsi_verify_attempts',
- default=3,
- help=_('Maximum attempts to verify an iSCSI connection is '
- 'active, sleeping 1 second between attempts.')),
cfg.StrOpt('http_url',
help='ironic-conductor node\'s HTTP server URL. '
'Example: http://192.1.2.3:8080',
@@ -95,6 +74,14 @@ deploy_opts = [
CONF = cfg.CONF
CONF.register_opts(deploy_opts, group='deploy')
+# TODO(Faizan): Move this logic to common/utils.py and deprecate
+# rootwrap_config.
+# This is required to set the default value of ironic_lib option
+# only if rootwrap_config does not contain the default value.
+if CONF.rootwrap_config != '/etc/ironic/rootwrap.conf':
+ root_helper = 'sudo ironic-rootwrap %s' % CONF.rootwrap_config
+ CONF.set_default('root_helper', root_helper, 'ironic_lib')
+
LOG = logging.getLogger(__name__)
VALID_ROOT_DEVICE_HINTS = set(('size', 'model', 'wwn', 'serial', 'vendor',
@@ -151,7 +138,7 @@ def check_file_system_for_iscsi_device(portal_address,
check_dir = "/dev/disk/by-path/ip-%s:%s-iscsi-%s-lun-1" % (portal_address,
portal_port,
target_iqn)
- total_checks = CONF.deploy.iscsi_verify_attempts
+ total_checks = CONF.disk_utils.iscsi_verify_attempts
for attempt in range(total_checks):
if os.path.exists(check_dir):
break
@@ -171,7 +158,7 @@ def verify_iscsi_connection(target_iqn):
"""Verify iscsi connection."""
LOG.debug("Checking for iSCSI target to become active.")
- for attempt in range(CONF.deploy.iscsi_verify_attempts):
+ for attempt in range(CONF.disk_utils.iscsi_verify_attempts):
out, _err = utils.execute('iscsiadm',
'-m', 'node',
'-S',
@@ -183,10 +170,10 @@ def verify_iscsi_connection(target_iqn):
LOG.debug("iSCSI connection not active. Rechecking. Attempt "
"%(attempt)d out of %(total)d",
{"attempt": attempt + 1,
- "total": CONF.deploy.iscsi_verify_attempts})
+ "total": CONF.disk_utils.iscsi_verify_attempts})
else:
msg = _("iSCSI connection did not become active after attempting to "
- "verify %d times.") % CONF.deploy.iscsi_verify_attempts
+ "verify %d times.") % CONF.disk_utils.iscsi_verify_attempts
LOG.error(msg)
raise exception.InstanceDeployFailure(msg)
@@ -231,163 +218,6 @@ 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.
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- 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, node_uuid, commit=True,
- boot_option="netboot", boot_mode="bios"):
- """Partition the disk device.
-
- Create partitions for root, swap, ephemeral and configdrive on a
- disk device.
-
- :param root_mb: Size of the root partition in mebibytes (MiB).
- :param swap_mb: Size of the swap partition in mebibytes (MiB). If 0,
- no partition will be created.
- :param ephemeral_mb: Size of the ephemeral partition in mebibytes (MiB).
- If 0, no partition will be created.
- :param configdrive_mb: Size of the configdrive partition in
- mebibytes (MiB). If 0, no partition will be created.
- :param commit: True/False. Default for this setting is True. If False
- partitions will not be written to disk.
- :param boot_option: Can be "local" or "netboot". "netboot" by default.
- :param boot_mode: Can be "bios" or "uefi". "bios" by default.
- :param node_uuid: Node's uuid. Used for logging.
- :returns: A dictionary containing the partition type as Key and partition
- path as Value for the partitions created by this method.
-
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- LOG.debug("Starting to partition the disk device: %(dev)s "
- "for node %(node)s",
- {'dev': dev, 'node': node_uuid})
- part_template = dev + '-part%d'
- part_dict = {}
-
- # For uefi localboot, switch partition table to gpt and create the efi
- # system partition as the first partition.
- if boot_mode == "uefi" and boot_option == "local":
- dp = disk_partitioner.DiskPartitioner(dev, disk_label="gpt")
- part_num = dp.add_partition(CONF.deploy.efi_system_partition_size,
- fs_type='fat32',
- bootable=True)
- part_dict['efi system partition'] = part_template % part_num
- else:
- dp = disk_partitioner.DiskPartitioner(dev)
-
- if ephemeral_mb:
- LOG.debug("Add ephemeral partition (%(size)d MB) to device: %(dev)s "
- "for node %(node)s",
- {'dev': dev, 'size': ephemeral_mb, 'node': node_uuid})
- part_num = dp.add_partition(ephemeral_mb)
- part_dict['ephemeral'] = part_template % part_num
- if swap_mb:
- LOG.debug("Add Swap partition (%(size)d MB) to device: %(dev)s "
- "for node %(node)s",
- {'dev': dev, 'size': swap_mb, 'node': node_uuid})
- part_num = dp.add_partition(swap_mb, fs_type='linux-swap')
- part_dict['swap'] = part_template % part_num
- if configdrive_mb:
- LOG.debug("Add config drive partition (%(size)d MB) to device: "
- "%(dev)s for node %(node)s",
- {'dev': dev, 'size': configdrive_mb, 'node': node_uuid})
- part_num = dp.add_partition(configdrive_mb)
- part_dict['configdrive'] = part_template % part_num
-
- # NOTE(lucasagomes): Make the root partition the last partition. This
- # enables tools like cloud-init's growroot utility to expand the root
- # partition until the end of the disk.
- LOG.debug("Add root partition (%(size)d MB) to device: %(dev)s "
- "for node %(node)s",
- {'dev': dev, 'size': root_mb, 'node': node_uuid})
- part_num = dp.add_partition(root_mb, bootable=(boot_option == "local" and
- boot_mode == "bios"))
- part_dict['root'] = part_template % part_num
-
- if commit:
- # write to the disk
- dp.commit()
- return part_dict
-
-
-def is_block_device(dev):
- """Check whether a device is block or not."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- attempts = CONF.deploy.iscsi_verify_attempts
- for attempt in range(attempts):
- try:
- s = os.stat(dev)
- except OSError as e:
- LOG.debug("Unable to stat device %(dev)s. Attempt %(attempt)d "
- "out of %(total)d. Error: %(err)s",
- {"dev": dev, "attempt": attempt + 1,
- "total": attempts, "err": e})
- time.sleep(1)
- else:
- return stat.S_ISBLK(s.st_mode)
- msg = _("Unable to stat device %(dev)s after attempting to verify "
- "%(attempts)d times.") % {'dev': dev, 'attempts': attempts}
- LOG.error(msg)
- raise exception.InstanceDeployFailure(msg)
-
-
-def dd(src, dst):
- """Execute dd from src to dst."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- utils.dd(src, dst, 'bs=%s' % CONF.deploy.dd_block_size, 'oflag=direct')
-
-
-def populate_image(src, dst):
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- data = images.qemu_img_info(src)
- if data.file_format == 'raw':
- dd(src, dst)
- else:
- images.convert_image(src, dst, 'raw', True)
-
-
-def block_uuid(dev):
- """Get UUID of a block device."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- out, _err = utils.execute('blkid', '-s', 'UUID', '-o', 'value', dev,
- run_as_root=True,
- check_exit_code=[0])
- return out.strip()
-
-
def _replace_lines_in_file(path, regex_pattern, replacement):
with open(path) as f:
lines = f.readlines()
@@ -468,278 +298,6 @@ def get_dev(address, port, iqn, lun):
return dev
-def get_image_mb(image_path, virtual_size=True):
- """Get size of an image in Megabyte."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- mb = 1024 * 1024
- if not virtual_size:
- image_byte = os.path.getsize(image_path)
- else:
- image_byte = images.converted_size(image_path)
- # round up size to MB
- image_mb = int((image_byte + mb - 1) / mb)
- return image_mb
-
-
-def get_dev_block_size(dev):
- """Get the device size in 512 byte sectors."""
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- block_sz, cmderr = utils.execute('blockdev', '--getsz', dev,
- run_as_root=True, check_exit_code=[0])
- return int(block_sz)
-
-
-def destroy_disk_metadata(dev, node_uuid):
- """Destroy metadata structures on node's disk.
-
- Ensure that node's disk appears to be blank without zeroing the entire
- drive. To do this we will zero:
- - the first 18KiB to clear MBR / GPT data
- - the last 18KiB to clear GPT and other metadata like: LVM, veritas,
- MDADM, DMRAID, ...
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
-
- # NOTE(NobodyCam): This is needed to work around bug:
- # https://bugs.launchpad.net/ironic/+bug/1317647
- LOG.debug("Start destroy disk metadata for node %(node)s.",
- {'node': node_uuid})
- try:
- utils.dd('/dev/zero', dev, 'bs=512', 'count=36')
- except processutils.ProcessExecutionError as err:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to erase beginning of disk for node "
- "%(node)s. Command: %(command)s. Error: %(error)s."),
- {'node': node_uuid,
- 'command': err.cmd,
- 'error': err.stderr})
-
- # now wipe the end of the disk.
- # get end of disk seek value
- try:
- block_sz = get_dev_block_size(dev)
- except processutils.ProcessExecutionError as err:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to get disk block count for node %(node)s. "
- "Command: %(command)s. Error: %(error)s."),
- {'node': node_uuid,
- 'command': err.cmd,
- 'error': err.stderr})
- else:
- seek_value = block_sz - 36
- try:
- utils.dd('/dev/zero', dev, 'bs=512', 'count=36',
- 'seek=%d' % seek_value)
- except processutils.ProcessExecutionError as err:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to erase the end of the disk on node "
- "%(node)s. Command: %(command)s. "
- "Error: %(error)s."),
- {'node': node_uuid,
- 'command': err.cmd,
- 'error': err.stderr})
- LOG.info(_LI("Disk metadata on %(dev)s successfully destroyed for node "
- "%(node)s"), {'dev': dev, 'node': node_uuid})
-
-
-def _get_configdrive(configdrive, node_uuid):
- """Get the information about size and location of the configdrive.
-
- :param configdrive: Base64 encoded Gzipped configdrive content or
- configdrive HTTP URL.
- :param node_uuid: Node's uuid. Used for logging.
- :raises: InstanceDeployFailure if it can't download or decode the
- config drive.
- :returns: A tuple with the size in MiB and path to the uncompressed
- configdrive file.
-
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
- # Check if the configdrive option is a HTTP URL or the content directly
- is_url = utils.is_http_url(configdrive)
- if is_url:
- try:
- data = requests.get(configdrive).content
- except requests.exceptions.RequestException as e:
- raise exception.InstanceDeployFailure(
- _("Can't download the configdrive content for node %(node)s "
- "from '%(url)s'. Reason: %(reason)s") %
- {'node': node_uuid, 'url': configdrive, 'reason': e})
- else:
- data = configdrive
-
- try:
- data = six.BytesIO(base64.b64decode(data))
- except TypeError:
- error_msg = (_('Config drive for node %s is not base64 encoded '
- 'or the content is malformed.') % node_uuid)
- if is_url:
- error_msg += _(' Downloaded from "%s".') % configdrive
- raise exception.InstanceDeployFailure(error_msg)
-
- configdrive_file = tempfile.NamedTemporaryFile(delete=False,
- prefix='configdrive',
- dir=CONF.tempdir)
- configdrive_mb = 0
- with gzip.GzipFile('configdrive', 'rb', fileobj=data) as gunzipped:
- try:
- shutil.copyfileobj(gunzipped, configdrive_file)
- except EnvironmentError as e:
- # Delete the created file
- utils.unlink_without_raise(configdrive_file.name)
- raise exception.InstanceDeployFailure(
- _('Encountered error while decompressing and writing '
- 'config drive for node %(node)s. Error: %(exc)s') %
- {'node': node_uuid, 'exc': e})
- else:
- # Get the file size and convert to MiB
- configdrive_file.seek(0, os.SEEK_END)
- bytes_ = configdrive_file.tell()
- configdrive_mb = int(math.ceil(float(bytes_) / units.Mi))
- finally:
- configdrive_file.close()
-
- return (configdrive_mb, configdrive_file.name)
-
-
-def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
- image_path, node_uuid, preserve_ephemeral=False,
- configdrive=None, boot_option="netboot",
- boot_mode="bios"):
- """Create partitions and copy an image to the root partition.
-
- :param dev: Path for the device to work on.
- :param root_mb: Size of the root partition in megabytes.
- :param swap_mb: Size of the swap partition in megabytes.
- :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0,
- no ephemeral partition will be created.
- :param ephemeral_format: The type of file system to format the ephemeral
- partition.
- :param image_path: Path for the instance's disk image.
- :param node_uuid: node's uuid. Used for logging.
- :param preserve_ephemeral: If True, no filesystem is written to the
- ephemeral block device, preserving whatever content it had (if the
- partition table has not changed).
- :param configdrive: Optional. Base64 encoded Gzipped configdrive content
- or configdrive HTTP URL.
- :param boot_option: Can be "local" or "netboot". "netboot" by default.
- :param boot_mode: Can be "bios" or "uefi". "bios" by default.
- :returns: a dictionary containing the following keys:
- 'root uuid': UUID of root partition
- 'efi system partition uuid': UUID of the uefi system partition
- (if boot mode is uefi).
- NOTE: If key exists but value is None, it means partition doesn't
- exist.
- """
- # NOTE(jlvillal): This function has been moved to ironic-lib. And is
- # planned to be deleted here. If need to modify this function, please also
- # do the same modification in ironic-lib
-
- # 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
- # now if we are committing the changes to disk clean first.
- if commit:
- destroy_disk_metadata(dev, node_uuid)
-
- try:
- # If requested, get the configdrive file and determine the size
- # of the configdrive partition
- configdrive_mb = 0
- configdrive_file = None
- if configdrive:
- configdrive_mb, configdrive_file = _get_configdrive(configdrive,
- node_uuid)
-
- part_dict = make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
- configdrive_mb, node_uuid,
- commit=commit,
- boot_option=boot_option,
- boot_mode=boot_mode)
- LOG.info(_LI("Successfully completed the disk device"
- " %(dev)s partitioning for node %(node)s"),
- {'dev': dev, "node": node_uuid})
-
- ephemeral_part = part_dict.get('ephemeral')
- swap_part = part_dict.get('swap')
- configdrive_part = part_dict.get('configdrive')
- root_part = part_dict.get('root')
-
- if not is_block_device(root_part):
- raise exception.InstanceDeployFailure(
- _("Root device '%s' not found") % root_part)
-
- for part in ('swap', 'ephemeral', 'configdrive',
- 'efi system partition'):
- part_device = part_dict.get(part)
- LOG.debug("Checking for %(part)s device (%(dev)s) on node "
- "%(node)s.",
- {'part': part, 'dev': part_device, 'node': node_uuid})
- if part_device and not is_block_device(part_device):
- raise exception.InstanceDeployFailure(
- _("'%(partition)s' device '%(part_device)s' not found") %
- {'partition': part, 'part_device': part_device})
-
- # If it's a uefi localboot, then we have created the efi system
- # partition. Create a fat filesystem on it.
- if boot_mode == "uefi" and boot_option == "local":
- efi_system_part = part_dict.get('efi system partition')
- utils.mkfs('vfat', efi_system_part, 'efi-part')
-
- if configdrive_part:
- # Copy the configdrive content to the configdrive partition
- dd(configdrive_file, configdrive_part)
- LOG.info(_LI("Configdrive for node %(node)s successfully copied "
- "onto partition %(partition)s"),
- {'node': node_uuid, 'partition': configdrive_part})
-
- finally:
- # If the configdrive was requested make sure we delete the file
- # after copying the content to the partition
- if configdrive_file:
- utils.unlink_without_raise(configdrive_file)
-
- populate_image(image_path, root_part)
- LOG.info(_LI("Image for %(node)s successfully populated"),
- {'node': node_uuid})
-
- if swap_part:
- utils.mkfs('swap', swap_part, 'swap1')
- LOG.info(_LI("Swap partition %(swap)s successfully formatted "
- "for node %(node)s"),
- {'swap': swap_part, 'node': node_uuid})
-
- if ephemeral_part and not preserve_ephemeral:
- utils.mkfs(ephemeral_format, ephemeral_part, "ephemeral0")
- LOG.info(_LI("Ephemeral partition %(ephemeral)s successfully "
- "formatted for node %(node)s"),
- {'ephemeral': ephemeral_part, 'node': node_uuid})
-
- uuids_to_return = {
- 'root uuid': root_part,
- 'efi system partition uuid': part_dict.get('efi system partition')
- }
-
- try:
- for part, part_dev in uuids_to_return.items():
- if part_dev:
- uuids_to_return[part] = block_uuid(part_dev)
-
- except processutils.ProcessExecutionError:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to detect %s"), part)
-
- return uuids_to_return
-
-
def deploy_partition_image(
address, port, iqn, lun, image_path,
root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid,
@@ -775,7 +333,7 @@ def deploy_partition_image(
NOTE: If key exists but value is None, it means partition doesn't
exist.
"""
- image_mb = get_image_mb(image_path)
+ image_mb = disk_utils.get_image_mb(image_path)
if image_mb > root_mb:
msg = (_('Root partition is too small for requested image. Image '
'virtual size: %(image_mb)d MB, Root size: %(root_mb)d MB')
@@ -783,7 +341,7 @@ def deploy_partition_image(
raise exception.InstanceDeployFailure(msg)
with _iscsi_setup_and_handle_errors(address, port, iqn, lun) as dev:
- uuid_dict_returned = work_on_disk(
+ uuid_dict_returned = disk_utils.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,
@@ -808,8 +366,8 @@ def deploy_disk_image(address, port, iqn, lun,
"""
with _iscsi_setup_and_handle_errors(address, port, iqn,
lun) as dev:
- populate_image(image_path, dev)
- disk_identifier = get_disk_identifier(dev)
+ disk_utils.populate_image(image_path, dev)
+ disk_identifier = disk_utils.get_disk_identifier(dev)
return {'disk identifier': disk_identifier}
@@ -826,7 +384,7 @@ def _iscsi_setup_and_handle_errors(address, port, iqn, lun):
dev = get_dev(address, port, iqn, lun)
discovery(address, port)
login_iscsi(address, port, iqn)
- if not is_block_device(dev):
+ if not disk_utils.is_block_device(dev):
raise exception.InstanceDeployFailure(_("Parent device '%s' not found")
% dev)
try:
diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py
index b2637467d..443fe0091 100644
--- a/ironic/drivers/modules/iscsi_deploy.py
+++ b/ironic/drivers/modules/iscsi_deploy.py
@@ -15,6 +15,7 @@
import os
+from ironic_lib import disk_utils
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import fileutils
@@ -241,7 +242,7 @@ def check_image_size(task):
"""
i_info = parse_instance_info(task.node)
image_path = _get_image_file_path(task.node.uuid)
- image_mb = deploy_utils.get_image_mb(image_path)
+ image_mb = disk_utils.get_image_mb(image_path)
root_mb = 1024 * int(i_info['root_gb'])
if image_mb > root_mb:
msg = (_('Root partition is too small for requested image. Image '
diff --git a/ironic/tests/unit/common/test_disk_partitioner.py b/ironic/tests/unit/common/test_disk_partitioner.py
deleted file mode 100644
index b32abbc40..000000000
--- a/ironic/tests/unit/common/test_disk_partitioner.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# Copyright 2014 Red Hat, Inc.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import eventlet
-import mock
-from testtools.matchers import HasLength
-
-from ironic.common import disk_partitioner
-from ironic.common import exception
-from ironic.common import utils
-from ironic.tests import base
-
-
-@mock.patch.object(eventlet.greenthread, 'sleep', lambda seconds: None)
-class DiskPartitionerTestCase(base.TestCase):
-
- def test_add_partition(self):
- dp = disk_partitioner.DiskPartitioner('/dev/fake')
- dp.add_partition(1024)
- dp.add_partition(512, fs_type='linux-swap')
- dp.add_partition(2048, bootable=True)
- expected = [(1, {'bootable': False,
- 'fs_type': '',
- 'type': 'primary',
- 'size': 1024}),
- (2, {'bootable': False,
- 'fs_type': 'linux-swap',
- 'type': 'primary',
- 'size': 512}),
- (3, {'bootable': True,
- 'fs_type': '',
- 'type': 'primary',
- 'size': 2048})]
- partitions = [(n, p) for n, p in dp.get_partitions()]
- self.assertThat(partitions, HasLength(3))
- self.assertEqual(expected, partitions)
-
- @mock.patch.object(disk_partitioner.DiskPartitioner, '_exec',
- autospec=True)
- @mock.patch.object(utils, 'execute', autospec=True)
- def test_commit(self, mock_utils_exc, mock_disk_partitioner_exec):
- dp = disk_partitioner.DiskPartitioner('/dev/fake')
- fake_parts = [(1, {'bootable': False,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1}),
- (2, {'bootable': True,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1})]
- with mock.patch.object(dp, 'get_partitions', autospec=True) as mock_gp:
- mock_gp.return_value = fake_parts
- mock_utils_exc.return_value = (None, None)
- dp.commit()
-
- mock_disk_partitioner_exec.assert_called_once_with(
- mock.ANY, 'mklabel', 'msdos',
- 'mkpart', 'fake-type', 'fake-fs-type', '1', '2',
- 'mkpart', 'fake-type', 'fake-fs-type', '2', '3',
- 'set', '2', 'boot', 'on')
- mock_utils_exc.assert_called_once_with(
- 'fuser', '/dev/fake', run_as_root=True, check_exit_code=[0, 1])
-
- @mock.patch.object(disk_partitioner.DiskPartitioner, '_exec',
- autospec=True)
- @mock.patch.object(utils, 'execute', autospec=True)
- def test_commit_with_device_is_busy_once(self, mock_utils_exc,
- mock_disk_partitioner_exec):
- dp = disk_partitioner.DiskPartitioner('/dev/fake')
- fake_parts = [(1, {'bootable': False,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1}),
- (2, {'bootable': True,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1})]
- fuser_outputs = iter([("/dev/fake: 10000 10001", None), (None, None)])
-
- with mock.patch.object(dp, 'get_partitions', autospec=True) as mock_gp:
- mock_gp.return_value = fake_parts
- mock_utils_exc.side_effect = fuser_outputs
- dp.commit()
-
- mock_disk_partitioner_exec.assert_called_once_with(
- mock.ANY, 'mklabel', 'msdos',
- 'mkpart', 'fake-type', 'fake-fs-type', '1', '2',
- 'mkpart', 'fake-type', 'fake-fs-type', '2', '3',
- 'set', '2', 'boot', 'on')
- mock_utils_exc.assert_called_with(
- 'fuser', '/dev/fake', run_as_root=True, check_exit_code=[0, 1])
- self.assertEqual(2, mock_utils_exc.call_count)
-
- @mock.patch.object(disk_partitioner.DiskPartitioner, '_exec',
- autospec=True)
- @mock.patch.object(utils, 'execute', autospec=True)
- def test_commit_with_device_is_always_busy(self, mock_utils_exc,
- mock_disk_partitioner_exec):
- dp = disk_partitioner.DiskPartitioner('/dev/fake')
- fake_parts = [(1, {'bootable': False,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1}),
- (2, {'bootable': True,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1})]
-
- with mock.patch.object(dp, 'get_partitions', autospec=True) as mock_gp:
- mock_gp.return_value = fake_parts
- mock_utils_exc.return_value = ("/dev/fake: 10000 10001", None)
- self.assertRaises(exception.InstanceDeployFailure, dp.commit)
-
- mock_disk_partitioner_exec.assert_called_once_with(
- mock.ANY, 'mklabel', 'msdos',
- 'mkpart', 'fake-type', 'fake-fs-type', '1', '2',
- 'mkpart', 'fake-type', 'fake-fs-type', '2', '3',
- 'set', '2', 'boot', 'on')
- mock_utils_exc.assert_called_with(
- 'fuser', '/dev/fake', run_as_root=True, check_exit_code=[0, 1])
- self.assertEqual(20, mock_utils_exc.call_count)
-
- @mock.patch.object(disk_partitioner.DiskPartitioner, '_exec',
- autospec=True)
- @mock.patch.object(utils, 'execute', autospec=True)
- def test_commit_with_device_disconnected(self, mock_utils_exc,
- mock_disk_partitioner_exec):
- dp = disk_partitioner.DiskPartitioner('/dev/fake')
- fake_parts = [(1, {'bootable': False,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1}),
- (2, {'bootable': True,
- 'fs_type': 'fake-fs-type',
- 'type': 'fake-type',
- 'size': 1})]
-
- with mock.patch.object(dp, 'get_partitions', autospec=True) as mock_gp:
- mock_gp.return_value = fake_parts
- mock_utils_exc.return_value = (None, "Specified filename /dev/fake"
- " does not exist.")
- self.assertRaises(exception.InstanceDeployFailure, dp.commit)
-
- mock_disk_partitioner_exec.assert_called_once_with(
- mock.ANY, 'mklabel', 'msdos',
- 'mkpart', 'fake-type', 'fake-fs-type', '1', '2',
- 'mkpart', 'fake-type', 'fake-fs-type', '2', '3',
- 'set', '2', 'boot', 'on')
- mock_utils_exc.assert_called_with(
- 'fuser', '/dev/fake', run_as_root=True, check_exit_code=[0, 1])
- self.assertEqual(20, mock_utils_exc.call_count)
-
-
-@mock.patch.object(utils, 'execute', autospec=True)
-class ListPartitionsTestCase(base.TestCase):
-
- def test_correct(self, execute_mock):
- output = """
-BYT;
-/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
-1:1.00MiB:501MiB:500MiB:ext4::boot;
-2:501MiB:476940MiB:476439MiB:::;
-"""
- expected = [
- {'number': 1, 'start': 1, 'end': 501, 'size': 500,
- 'filesystem': 'ext4', 'flags': 'boot'},
- {'number': 2, 'start': 501, 'end': 476940, 'size': 476439,
- 'filesystem': '', 'flags': ''},
- ]
- execute_mock.return_value = (output, '')
- result = disk_partitioner.list_partitions('/dev/fake')
- self.assertEqual(expected, result)
- execute_mock.assert_called_once_with(
- 'parted', '-s', '-m', '/dev/fake', 'unit', 'MiB', 'print',
- use_standard_locale=True, run_as_root=True)
-
- @mock.patch.object(disk_partitioner.LOG, 'warning', autospec=True)
- def test_incorrect(self, log_mock, execute_mock):
- output = """
-BYT;
-/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
-1:XX1076MiB:---:524MiB:ext4::boot;
-"""
- execute_mock.return_value = (output, '')
- self.assertEqual([], disk_partitioner.list_partitions('/dev/fake'))
- self.assertEqual(1, log_mock.call_count)
diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
index 41ffd0dba..4f8c3ca34 100644
--- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
@@ -14,28 +14,22 @@
# License for the specific language governing permissions and limitations
# under the License.
-import base64
-import gzip
import os
-import shutil
-import stat
import tempfile
import time
import types
+from ironic_lib import disk_utils
+from ironic_lib import utils as lib_utils
import mock
-from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_utils import uuidutils
-import requests
import testtools
from testtools import matchers
from ironic.common import boot_devices
-from ironic.common import disk_partitioner
from ironic.common import exception
from ironic.common import image_service
-from ironic.common import images
from ironic.common import keystone
from ironic.common import states
from ironic.common import utils as common_utils
@@ -329,8 +323,8 @@ menuentry "boot_whole_disk" {
@mock.patch.object(time, 'sleep', lambda seconds: None)
class PhysicalWorkTestCase(tests_base.TestCase):
- def _mock_calls(self, name_list):
- patch_list = [mock.patch.object(utils, name,
+ def _mock_calls(self, name_list, module):
+ patch_list = [mock.patch.object(module, name,
spec_set=types.FunctionType)
for name in name_list]
mock_list = [patcher.start() for patcher in patch_list]
@@ -342,8 +336,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
parent_mock.attach_mock(mocker, name)
return parent_mock
- @mock.patch.object(common_utils, 'mkfs', autospec=True)
- def _test_deploy_partition_image(self, mock_mkfs, boot_option=None,
+ def _test_deploy_partition_image(self, boot_option=None,
boot_mode=None):
"""Check loosely all functions are called with right args."""
address = '127.0.0.1'
@@ -363,17 +356,22 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_part = '/dev/fake-part2'
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'block_uuid',
- 'notify', 'destroy_disk_metadata']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
- parent_mock.block_uuid.return_value = root_uuid
- parent_mock.make_partitions.return_value = {'root': root_part,
- 'swap': swap_part}
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi', 'notify']
+
+ disk_utils_name_list = ['is_block_device', 'get_image_mb',
+ 'make_partitions', 'populate_image', 'mkfs',
+ 'block_uuid', 'destroy_disk_metadata']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
+ disk_utils_mock.block_uuid.return_value = root_uuid
+ disk_utils_mock.make_partitions.return_value = {'root': root_part,
+ 'swap': swap_part}
make_partitions_expected_args = [dev, root_mb, swap_mb, ephemeral_mb,
configdrive_mb, node_uuid]
@@ -393,32 +391,37 @@ class PhysicalWorkTestCase(tests_base.TestCase):
make_partitions_expected_kwargs['boot_mode'] = 'bios'
# If no boot_option, then it should default to netboot.
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.destroy_disk_metadata(dev, node_uuid),
- mock.call.make_partitions(
- *make_partitions_expected_args,
- **make_partitions_expected_kwargs),
- mock.call.is_block_device(root_part),
- mock.call.is_block_device(swap_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.block_uuid(root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.destroy_disk_metadata(
+ dev, node_uuid),
+ mock.call.make_partitions(
+ *make_partitions_expected_args,
+ **make_partitions_expected_kwargs),
+ mock.call.is_block_device(root_part),
+ mock.call.is_block_device(swap_part),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.mkfs(
+ dev=swap_part, fs='swap',
+ label='swap1'),
+ mock.call.block_uuid(root_part)]
uuids_dict_returned = utils.deploy_partition_image(
address, port, iqn, lun, image_path, root_mb, swap_mb,
ephemeral_mb, ephemeral_format, node_uuid, **deploy_kwargs)
- self.assertEqual(calls_expected, parent_mock.mock_calls)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
expected_uuid_dict = {
'root uuid': root_uuid,
'efi system partition uuid': None}
self.assertEqual(expected_uuid_dict, uuids_dict_returned)
- mock_mkfs.assert_called_once_with('swap', swap_part, 'swap1')
def test_deploy_partition_image_without_boot_option(self):
self._test_deploy_partition_image()
@@ -444,7 +447,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
self._test_deploy_partition_image(boot_option="netboot",
boot_mode="uefi")
- @mock.patch.object(utils, 'get_image_mb', return_value=129, autospec=True)
+ @mock.patch.object(disk_utils, 'get_image_mb', return_value=129,
+ autospec=True)
def test_deploy_partition_image_image_exceeds_root_partition(self,
gim_mock):
address = '127.0.0.1'
@@ -467,10 +471,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
# We mock utils.block_uuid separately here because we can't predict
# the order in which it will be called.
- @mock.patch.object(utils, 'block_uuid', autospec=True)
- @mock.patch.object(common_utils, 'mkfs', autospec=True)
- def test_deploy_partition_image_localboot_uefi(self, mock_mkfs,
- block_uuid_mock):
+ @mock.patch.object(disk_utils, 'block_uuid', autospec=True)
+ def test_deploy_partition_image_localboot_uefi(self, block_uuid_mock):
"""Check loosely all functions are called with right args."""
address = '127.0.0.1'
port = 3306
@@ -491,14 +493,19 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
efi_system_part_uuid = '9036-482'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'notify',
- 'destroy_disk_metadata']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi', 'notify']
+
+ disk_utils_name_list = ['get_image_mb', 'make_partitions',
+ 'is_block_device', 'populate_image', 'mkfs',
+ 'destroy_disk_metadata']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
def block_uuid_side_effect(device):
if device == root_part:
@@ -507,46 +514,56 @@ class PhysicalWorkTestCase(tests_base.TestCase):
return efi_system_part_uuid
block_uuid_mock.side_effect = block_uuid_side_effect
- parent_mock.make_partitions.return_value = {
+ disk_utils_mock.make_partitions.return_value = {
'root': root_part, 'swap': swap_part,
'efi system partition': efi_system_part}
# If no boot_option, then it should default to netboot.
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.destroy_disk_metadata(dev, node_uuid),
- mock.call.make_partitions(dev, root_mb, swap_mb,
- ephemeral_mb,
- configdrive_mb,
- node_uuid,
- commit=True,
- boot_option="local",
- boot_mode="uefi"),
- mock.call.is_block_device(root_part),
- mock.call.is_block_device(swap_part),
- mock.call.is_block_device(efi_system_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.destroy_disk_metadata(
+ dev, node_uuid),
+ mock.call.make_partitions(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ configdrive_mb,
+ node_uuid,
+ commit=True,
+ boot_option="local",
+ boot_mode="uefi"),
+ mock.call.is_block_device(root_part),
+ mock.call.is_block_device(swap_part),
+ mock.call.is_block_device(
+ efi_system_part),
+ mock.call.mkfs(
+ dev=efi_system_part, fs='vfat',
+ label='efi-part'),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.mkfs(
+ dev=swap_part, fs='swap',
+ label='swap1')]
uuid_dict_returned = utils.deploy_partition_image(
address, port, iqn, lun, image_path, root_mb, swap_mb,
ephemeral_mb, ephemeral_format, node_uuid, boot_option="local",
boot_mode="uefi")
- self.assertEqual(calls_expected, parent_mock.mock_calls)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
+
block_uuid_mock.assert_any_call('/dev/fake-part1')
block_uuid_mock.assert_any_call('/dev/fake-part3')
expected_uuid_dict = {
'root uuid': root_uuid,
'efi system partition uuid': efi_system_part_uuid}
self.assertEqual(expected_uuid_dict, uuid_dict_returned)
- expected_calls = [mock.call('vfat', efi_system_part, 'efi-part'),
- mock.call('swap', swap_part, 'swap1')]
- mock_mkfs.assert_has_calls(expected_calls)
def test_deploy_partition_image_without_swap(self):
"""Check loosely all functions are called with right args."""
@@ -566,34 +583,42 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_part = '/dev/fake-part1'
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'block_uuid',
- 'notify', 'destroy_disk_metadata']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
- parent_mock.block_uuid.return_value = root_uuid
- parent_mock.make_partitions.return_value = {'root': root_part}
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.destroy_disk_metadata(dev, node_uuid),
- mock.call.make_partitions(dev, root_mb, swap_mb,
- ephemeral_mb,
- configdrive_mb,
- node_uuid,
- commit=True,
- boot_option="netboot",
- boot_mode="bios"),
- mock.call.is_block_device(root_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.block_uuid(root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'notify', 'logout_iscsi', 'delete_iscsi']
+
+ disk_utils_name_list = ['make_partitions', 'get_image_mb',
+ 'is_block_device', 'populate_image',
+ 'block_uuid', 'destroy_disk_metadata']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
+ disk_utils_mock.block_uuid.return_value = root_uuid
+ disk_utils_mock.make_partitions.return_value = {'root': root_part}
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.destroy_disk_metadata(
+ dev, node_uuid),
+ mock.call.make_partitions(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ configdrive_mb,
+ node_uuid,
+ commit=True,
+ boot_option="netboot",
+ boot_mode="bios"),
+ mock.call.is_block_device(root_part),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.block_uuid(root_part)]
uuid_dict_returned = utils.deploy_partition_image(address, port, iqn,
lun, image_path,
@@ -602,11 +627,11 @@ class PhysicalWorkTestCase(tests_base.TestCase):
ephemeral_format,
node_uuid)
- self.assertEqual(calls_expected, parent_mock.mock_calls)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
self.assertEqual(root_uuid, uuid_dict_returned['root uuid'])
- @mock.patch.object(common_utils, 'mkfs', autospec=True)
- def test_deploy_partition_image_with_ephemeral(self, mock_mkfs):
+ def test_deploy_partition_image_with_ephemeral(self):
"""Check loosely all functions are called with right args."""
address = '127.0.0.1'
port = 3306
@@ -626,37 +651,54 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_part = '/dev/fake-part3'
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'block_uuid',
- 'notify', 'destroy_disk_metadata']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
- parent_mock.block_uuid.return_value = root_uuid
- parent_mock.make_partitions.return_value = {
- 'swap': swap_part, 'ephemeral': ephemeral_part, 'root': root_part}
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.destroy_disk_metadata(dev, node_uuid),
- mock.call.make_partitions(dev, root_mb, swap_mb,
- ephemeral_mb,
- configdrive_mb,
- node_uuid,
- commit=True,
- boot_option="netboot",
- boot_mode="bios"),
- mock.call.is_block_device(root_part),
- mock.call.is_block_device(swap_part),
- mock.call.is_block_device(ephemeral_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.block_uuid(root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi', 'notify']
+
+ disk_utils_name_list = ['get_image_mb', 'make_partitions',
+ 'is_block_device', 'populate_image', 'mkfs',
+ 'block_uuid', 'destroy_disk_metadata']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
+ disk_utils_mock.block_uuid.return_value = root_uuid
+ disk_utils_mock.make_partitions.return_value = {
+ 'swap': swap_part,
+ 'ephemeral': ephemeral_part,
+ 'root': root_part}
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.destroy_disk_metadata(
+ dev, node_uuid),
+ mock.call.make_partitions(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ configdrive_mb,
+ node_uuid,
+ commit=True,
+ boot_option="netboot",
+ boot_mode="bios"),
+ mock.call.is_block_device(root_part),
+ mock.call.is_block_device(swap_part),
+ mock.call.is_block_device(ephemeral_part),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.mkfs(
+ dev=swap_part, fs='swap',
+ label='swap1'),
+ mock.call.mkfs(
+ dev=ephemeral_part,
+ fs=ephemeral_format,
+ label='ephemeral0'),
+ mock.call.block_uuid(root_part)]
uuid_dict_returned = utils.deploy_partition_image(address, port, iqn,
lun, image_path,
@@ -665,15 +707,11 @@ class PhysicalWorkTestCase(tests_base.TestCase):
ephemeral_format,
node_uuid)
- self.assertEqual(calls_expected, parent_mock.mock_calls)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
self.assertEqual(root_uuid, uuid_dict_returned['root uuid'])
- expected_calls = [mock.call('swap', swap_part, 'swap1'),
- mock.call(ephemeral_format, ephemeral_part,
- 'ephemeral0')]
- mock_mkfs.assert_has_calls(expected_calls)
- @mock.patch.object(common_utils, 'mkfs', autospec=True)
- def test_deploy_partition_image_preserve_ephemeral(self, mock_mkfs):
+ def test_deploy_partition_image_preserve_ephemeral(self):
"""Check if all functions are called with right args."""
address = '127.0.0.1'
port = 3306
@@ -693,48 +731,60 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_part = '/dev/fake-part3'
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'block_uuid',
- 'notify', 'get_dev_block_size']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
- parent_mock.block_uuid.return_value = root_uuid
- parent_mock.make_partitions.return_value = {
- 'swap': swap_part, 'ephemeral': ephemeral_part, 'root': root_part}
- parent_mock.block_uuid.return_value = root_uuid
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.make_partitions(dev, root_mb, swap_mb,
- ephemeral_mb,
- configdrive_mb,
- node_uuid,
- commit=False,
- boot_option="netboot",
- boot_mode="bios"),
- mock.call.is_block_device(root_part),
- mock.call.is_block_device(swap_part),
- mock.call.is_block_device(ephemeral_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.block_uuid(root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'delete_iscsi', 'logout_iscsi', 'notify']
+ disk_utils_name_list = ['make_partitions', 'get_image_mb',
+ 'is_block_device', 'populate_image', 'mkfs',
+ 'block_uuid', 'get_dev_block_size']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
+ disk_utils_mock.block_uuid.return_value = root_uuid
+ disk_utils_mock.make_partitions.return_value = {
+ 'swap': swap_part,
+ 'ephemeral': ephemeral_part,
+ 'root': root_part}
+ disk_utils_mock.block_uuid.return_value = root_uuid
+
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.make_partitions(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ configdrive_mb,
+ node_uuid,
+ commit=False,
+ boot_option="netboot",
+ boot_mode="bios"),
+ mock.call.is_block_device(root_part),
+ mock.call.is_block_device(swap_part),
+ mock.call.is_block_device(ephemeral_part),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.mkfs(
+ dev=swap_part, fs='swap',
+ label='swap1'),
+ mock.call.block_uuid(root_part)]
uuid_dict_returned = 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.get_dev_block_size.called)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
+ self.assertFalse(disk_utils_mock.get_dev_block_size.called)
self.assertEqual(root_uuid, uuid_dict_returned['root uuid'])
- mock_mkfs.assert_called_once_with('swap', swap_part, 'swap1')
- @mock.patch.object(common_utils, 'unlink_without_raise', autospec=True)
+ @mock.patch.object(lib_utils, 'unlink_without_raise', autospec=True)
def test_deploy_partition_image_with_configdrive(self, mock_unlink):
"""Check loosely all functions are called with right args."""
address = '127.0.0.1'
@@ -755,53 +805,64 @@ class PhysicalWorkTestCase(tests_base.TestCase):
root_part = '/dev/fake-part2'
root_uuid = '12345678-1234-1234-12345678-12345678abcdef'
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'make_partitions',
- 'is_block_device', 'populate_image', 'block_uuid',
- 'notify', 'destroy_disk_metadata', 'dd',
- '_get_configdrive']
- parent_mock = self._mock_calls(name_list)
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.is_block_device.return_value = True
- parent_mock.block_uuid.return_value = root_uuid
- parent_mock.make_partitions.return_value = {'root': root_part,
- 'configdrive':
- configdrive_part}
- parent_mock._get_configdrive.return_value = (10, 'configdrive-path')
- calls_expected = [mock.call.get_image_mb(image_path),
- 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.destroy_disk_metadata(dev, node_uuid),
- mock.call._get_configdrive(configdrive_url,
- node_uuid),
- mock.call.make_partitions(dev, root_mb, swap_mb,
- ephemeral_mb,
- configdrive_mb,
- node_uuid,
- commit=True,
- boot_option="netboot",
- boot_mode="bios"),
- mock.call.is_block_device(root_part),
- mock.call.is_block_device(configdrive_part),
- mock.call.dd(mock.ANY, configdrive_part),
- mock.call.populate_image(image_path, root_part),
- mock.call.block_uuid(root_part),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi', 'notify']
+ disk_utils_name_list = ['is_block_device', 'populate_image',
+ 'get_image_mb', 'destroy_disk_metadata', 'dd',
+ 'block_uuid', 'make_partitions',
+ '_get_configdrive']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.is_block_device.return_value = True
+ disk_utils_mock.block_uuid.return_value = root_uuid
+ disk_utils_mock.make_partitions.return_value = {
+ 'root': root_part,
+ 'configdrive': configdrive_part}
+ disk_utils_mock._get_configdrive.return_value = (10,
+ 'configdrive-path')
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.is_block_device(dev),
+ mock.call.destroy_disk_metadata(
+ dev, node_uuid),
+ mock.call._get_configdrive(
+ configdrive_url, node_uuid,
+ tempdir=None),
+ mock.call.make_partitions(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ configdrive_mb,
+ node_uuid,
+ commit=True,
+ boot_option="netboot",
+ boot_mode="bios"),
+ mock.call.is_block_device(root_part),
+ mock.call.is_block_device(
+ configdrive_part),
+ mock.call.dd(mock.ANY, configdrive_part),
+ mock.call.populate_image(
+ image_path, root_part),
+ mock.call.block_uuid(root_part)]
uuid_dict_returned = 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(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
self.assertEqual(root_uuid, uuid_dict_returned['root uuid'])
mock_unlink.assert_called_once_with('configdrive-path')
- @mock.patch.object(utils, 'get_disk_identifier', autospec=True)
+ @mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True)
def test_deploy_whole_disk_image(self, mock_gdi):
"""Check loosely all functions are called with right args."""
address = '127.0.0.1'
@@ -812,25 +873,29 @@ class PhysicalWorkTestCase(tests_base.TestCase):
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
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi', 'notify']
+ disk_utils_name_list = ['is_block_device', 'populate_image']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_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_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.is_block_device(dev),
+ mock.call.populate_image(image_path, dev)]
uuid_dict_returned = utils.deploy_disk_image(address, port, iqn, lun,
image_path, node_uuid)
- self.assertEqual(calls_expected, parent_mock.mock_calls)
+ self.assertEqual(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
self.assertEqual('0x12345678', uuid_dict_returned['disk identifier'])
@mock.patch.object(common_utils, 'execute', autospec=True)
@@ -921,7 +986,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)
+ @mock.patch.object(disk_utils, 'is_block_device', lambda d: True)
def test_always_logout_and_delete_iscsi(self):
"""Check if logout_iscsi() and delete_iscsi() are called.
@@ -945,42 +1010,39 @@ class PhysicalWorkTestCase(tests_base.TestCase):
class TestException(Exception):
pass
- name_list = ['get_dev', 'get_image_mb', 'discovery', 'login_iscsi',
- 'logout_iscsi', 'delete_iscsi', 'work_on_disk']
- patch_list = [mock.patch.object(utils, name,
- spec_set=types.FunctionType)
- for name in name_list]
- mock_list = [patcher.start() for patcher in patch_list]
- for patcher in patch_list:
- self.addCleanup(patcher.stop)
-
- parent_mock = mock.MagicMock(spec=[])
- for mocker, name in zip(mock_list, name_list):
- parent_mock.attach_mock(mocker, name)
-
- parent_mock.get_dev.return_value = dev
- parent_mock.get_image_mb.return_value = 1
- parent_mock.work_on_disk.side_effect = TestException
- calls_expected = [mock.call.get_image_mb(image_path),
- mock.call.get_dev(address, port, iqn, lun),
- mock.call.discovery(address, port),
- mock.call.login_iscsi(address, port, iqn),
- mock.call.work_on_disk(dev, root_mb, swap_mb,
- ephemeral_mb,
- ephemeral_format, image_path,
- node_uuid, configdrive=None,
- preserve_ephemeral=False,
- boot_option="netboot",
- boot_mode="bios"),
- mock.call.logout_iscsi(address, port, iqn),
- mock.call.delete_iscsi(address, port, iqn)]
+ utils_name_list = ['get_dev', 'discovery', 'login_iscsi',
+ 'logout_iscsi', 'delete_iscsi']
+
+ disk_utils_name_list = ['get_image_mb', 'work_on_disk']
+
+ utils_mock = self._mock_calls(utils_name_list, utils)
+ utils_mock.get_dev.return_value = dev
+
+ disk_utils_mock = self._mock_calls(disk_utils_name_list, disk_utils)
+ disk_utils_mock.get_image_mb.return_value = 1
+ disk_utils_mock.work_on_disk.side_effect = TestException
+ utils_calls_expected = [mock.call.get_dev(address, port, iqn, lun),
+ mock.call.discovery(address, port),
+ mock.call.login_iscsi(address, port, iqn),
+ mock.call.logout_iscsi(address, port, iqn),
+ mock.call.delete_iscsi(address, port, iqn)]
+ disk_utils_calls_expected = [mock.call.get_image_mb(image_path),
+ mock.call.work_on_disk(
+ dev, root_mb, swap_mb,
+ ephemeral_mb,
+ ephemeral_format, image_path,
+ node_uuid, configdrive=None,
+ preserve_ephemeral=False,
+ boot_option="netboot",
+ boot_mode="bios")]
self.assertRaises(TestException, 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(utils_calls_expected, utils_mock.mock_calls)
+ self.assertEqual(disk_utils_calls_expected, disk_utils_mock.mock_calls)
class SwitchPxeConfigTestCase(tests_base.TestCase):
@@ -1114,45 +1176,6 @@ class OtherFunctionTestCase(db_base.DbTestCase):
actual = utils.get_dev('1.2.3.4', 5678, 'iqn.fake', 9)
self.assertEqual(expected, actual)
- @mock.patch.object(os, 'stat', autospec=True)
- @mock.patch.object(stat, 'S_ISBLK', autospec=True)
- def test_is_block_device_works(self, mock_is_blk, mock_os):
- device = '/dev/disk/by-path/ip-1.2.3.4:5678-iscsi-iqn.fake-lun-9'
- mock_is_blk.return_value = True
- mock_os().st_mode = 10000
- self.assertTrue(utils.is_block_device(device))
- mock_is_blk.assert_called_once_with(mock_os().st_mode)
-
- @mock.patch.object(os, 'stat', autospec=True)
- def test_is_block_device_raises(self, mock_os):
- device = '/dev/disk/by-path/ip-1.2.3.4:5678-iscsi-iqn.fake-lun-9'
- mock_os.side_effect = OSError
- self.assertRaises(exception.InstanceDeployFailure,
- utils.is_block_device, device)
- mock_os.assert_has_calls([mock.call(device)] * 3)
-
- @mock.patch.object(os.path, 'getsize', autospec=True)
- @mock.patch.object(images, 'converted_size', autospec=True)
- def test_get_image_mb(self, mock_csize, mock_getsize):
- mb = 1024 * 1024
-
- mock_getsize.return_value = 0
- mock_csize.return_value = 0
- self.assertEqual(0, utils.get_image_mb('x', False))
- self.assertEqual(0, utils.get_image_mb('x', True))
- mock_getsize.return_value = 1
- mock_csize.return_value = 1
- self.assertEqual(1, utils.get_image_mb('x', False))
- self.assertEqual(1, utils.get_image_mb('x', True))
- mock_getsize.return_value = mb
- mock_csize.return_value = mb
- self.assertEqual(1, utils.get_image_mb('x', False))
- self.assertEqual(1, utils.get_image_mb('x', True))
- mock_getsize.return_value = mb + 1
- mock_csize.return_value = mb + 1
- self.assertEqual(2, utils.get_image_mb('x', False))
- self.assertEqual(2, utils.get_image_mb('x', True))
-
def test_parse_root_device_hints(self):
self.node.properties['root_device'] = {
'wwn': 123456, 'model': 'foo-model', 'size': 123,
@@ -1233,351 +1256,6 @@ class OtherFunctionTestCase(db_base.DbTestCase):
result = utils.get_boot_option(self.node)
self.assertEqual("netboot", result)
-
-@mock.patch.object(disk_partitioner.DiskPartitioner, 'commit', lambda _: None)
-class WorkOnDiskTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(WorkOnDiskTestCase, self).setUp()
- self.image_path = '/tmp/xyz/image'
- self.root_mb = 128
- self.swap_mb = 64
- self.ephemeral_mb = 0
- self.ephemeral_format = None
- self.configdrive_mb = 0
- self.dev = '/dev/fake'
- self.swap_part = '/dev/fake-part1'
- self.root_part = '/dev/fake-part2'
-
- self.mock_ibd_obj = mock.patch.object(
- utils, 'is_block_device', autospec=True)
- self.mock_ibd = self.mock_ibd_obj.start()
- self.addCleanup(self.mock_ibd_obj.stop)
- self.mock_mp_obj = mock.patch.object(
- utils, 'make_partitions', autospec=True)
- self.mock_mp = self.mock_mp_obj.start()
- self.addCleanup(self.mock_mp_obj.stop)
- self.mock_remlbl_obj = mock.patch.object(
- utils, 'destroy_disk_metadata', autospec=True)
- self.mock_remlbl = self.mock_remlbl_obj.start()
- self.addCleanup(self.mock_remlbl_obj.stop)
- self.mock_mp.return_value = {'swap': self.swap_part,
- 'root': self.root_part}
-
- 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.root_part)
- self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
- self.swap_mb, self.ephemeral_mb,
- self.configdrive_mb,
- 'fake-uuid',
- commit=True,
- boot_option="netboot",
- boot_mode="bios")
-
- def test_no_swap_partition(self):
- self.mock_ibd.side_effect = iter([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,
- 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_mp.assert_called_once_with(self.dev, self.root_mb,
- self.swap_mb, self.ephemeral_mb,
- self.configdrive_mb,
- 'fake-uuid',
- commit=True,
- boot_option="netboot",
- boot_mode="bios")
-
- def test_no_ephemeral_partition(self):
- ephemeral_part = '/dev/fake-part1'
- swap_part = '/dev/fake-part2'
- root_part = '/dev/fake-part3'
- ephemeral_mb = 256
- ephemeral_format = 'exttest'
-
- self.mock_mp.return_value = {'ephemeral': ephemeral_part,
- 'swap': swap_part,
- 'root': root_part}
- self.mock_ibd.side_effect = iter([True, True, False])
- calls = [mock.call(root_part),
- mock.call(swap_part),
- mock.call(ephemeral_part)]
- self.assertRaises(exception.InstanceDeployFailure,
- utils.work_on_disk, self.dev, self.root_mb,
- self.swap_mb, ephemeral_mb, ephemeral_format,
- self.image_path, 'fake-uuid')
- self.assertEqual(self.mock_ibd.call_args_list, calls)
- self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
- self.swap_mb, ephemeral_mb,
- self.configdrive_mb,
- 'fake-uuid',
- commit=True,
- boot_option="netboot",
- boot_mode="bios")
-
- @mock.patch.object(common_utils, 'unlink_without_raise', autospec=True)
- @mock.patch.object(utils, '_get_configdrive', autospec=True)
- def test_no_configdrive_partition(self, mock_configdrive, mock_unlink):
- mock_configdrive.return_value = (10, 'fake-path')
- swap_part = '/dev/fake-part1'
- configdrive_part = '/dev/fake-part2'
- root_part = '/dev/fake-part3'
- configdrive_url = 'http://1.2.3.4/cd'
- configdrive_mb = 10
-
- self.mock_mp.return_value = {'swap': swap_part,
- 'configdrive': configdrive_part,
- 'root': root_part}
- self.mock_ibd.side_effect = iter([True, True, False])
- calls = [mock.call(root_part),
- mock.call(swap_part),
- mock.call(configdrive_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',
- preserve_ephemeral=False,
- configdrive=configdrive_url,
- boot_option="netboot")
- self.assertEqual(self.mock_ibd.call_args_list, calls)
- self.mock_mp.assert_called_once_with(self.dev, self.root_mb,
- self.swap_mb, self.ephemeral_mb,
- configdrive_mb,
- 'fake-uuid',
- commit=True,
- boot_option="netboot",
- boot_mode="bios")
- mock_unlink.assert_called_once_with('fake-path')
-
-
-@mock.patch.object(common_utils, 'execute', autospec=True)
-class MakePartitionsTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(MakePartitionsTestCase, self).setUp()
- self.dev = 'fake-dev'
- self.root_mb = 1024
- self.swap_mb = 512
- self.ephemeral_mb = 0
- self.configdrive_mb = 0
- self.parted_static_cmd = ['parted', '-a', 'optimal', '-s', self.dev,
- '--', 'unit', 'MiB', 'mklabel', 'msdos']
-
- def _test_make_partitions(self, mock_exc, boot_option):
- mock_exc.return_value = (None, None)
- utils.make_partitions(self.dev, self.root_mb, self.swap_mb,
- self.ephemeral_mb, self.configdrive_mb,
- '12345678-1234-1234-1234-1234567890abcxyz',
- boot_option=boot_option)
-
- expected_mkpart = ['mkpart', 'primary', 'linux-swap', '1', '513',
- 'mkpart', 'primary', '', '513', '1537']
- if boot_option == "local":
- expected_mkpart.extend(['set', '2', 'boot', 'on'])
- parted_cmd = self.parted_static_cmd + expected_mkpart
- parted_call = mock.call(*parted_cmd, use_standard_locale=True,
- run_as_root=True, check_exit_code=[0])
- fuser_cmd = ['fuser', 'fake-dev']
- fuser_call = mock.call(*fuser_cmd, run_as_root=True,
- check_exit_code=[0, 1])
- mock_exc.assert_has_calls([parted_call, fuser_call])
-
- def test_make_partitions(self, mock_exc):
- self._test_make_partitions(mock_exc, boot_option="netboot")
-
- def test_make_partitions_local_boot(self, mock_exc):
- self._test_make_partitions(mock_exc, boot_option="local")
-
- def test_make_partitions_with_ephemeral(self, mock_exc):
- self.ephemeral_mb = 2048
- expected_mkpart = ['mkpart', 'primary', '', '1', '2049',
- 'mkpart', 'primary', 'linux-swap', '2049', '2561',
- 'mkpart', 'primary', '', '2561', '3585']
- cmd = self.parted_static_cmd + expected_mkpart
- mock_exc.return_value = (None, None)
- utils.make_partitions(self.dev, self.root_mb, self.swap_mb,
- self.ephemeral_mb, self.configdrive_mb,
- '12345678-1234-1234-1234-1234567890abcxyz')
-
- parted_call = mock.call(*cmd, use_standard_locale=True,
- run_as_root=True, check_exit_code=[0])
- mock_exc.assert_has_calls([parted_call])
-
-
-@mock.patch.object(utils, 'get_dev_block_size', autospec=True)
-@mock.patch.object(common_utils, 'execute', autospec=True)
-class DestroyMetaDataTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(DestroyMetaDataTestCase, self).setUp()
- self.dev = 'fake-dev'
- self.node_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
-
- def test_destroy_disk_metadata(self, mock_exec, mock_gz):
- mock_gz.return_value = 64
- expected_calls = [mock.call('dd', 'if=/dev/zero', 'of=fake-dev',
- 'bs=512', 'count=36', run_as_root=True,
- check_exit_code=[0],
- use_standard_locale=True),
- mock.call('dd', 'if=/dev/zero', 'of=fake-dev',
- 'bs=512', 'count=36', 'seek=28',
- run_as_root=True,
- check_exit_code=[0],
- use_standard_locale=True)]
- utils.destroy_disk_metadata(self.dev, self.node_uuid)
- mock_exec.assert_has_calls(expected_calls)
- self.assertTrue(mock_gz.called)
-
- def test_destroy_disk_metadata_get_dev_size_fail(self, mock_exec, mock_gz):
- mock_gz.side_effect = processutils.ProcessExecutionError
-
- expected_call = [mock.call('dd', 'if=/dev/zero', 'of=fake-dev',
- 'bs=512', 'count=36', run_as_root=True,
- check_exit_code=[0],
- use_standard_locale=True)]
- self.assertRaises(processutils.ProcessExecutionError,
- utils.destroy_disk_metadata,
- self.dev,
- self.node_uuid)
- mock_exec.assert_has_calls(expected_call)
-
- def test_destroy_disk_metadata_dd_fail(self, mock_exec, mock_gz):
- mock_exec.side_effect = processutils.ProcessExecutionError
-
- expected_call = [mock.call('dd', 'if=/dev/zero', 'of=fake-dev',
- 'bs=512', 'count=36', run_as_root=True,
- check_exit_code=[0],
- use_standard_locale=True)]
- self.assertRaises(processutils.ProcessExecutionError,
- utils.destroy_disk_metadata,
- self.dev,
- self.node_uuid)
- mock_exec.assert_has_calls(expected_call)
- self.assertFalse(mock_gz.called)
-
-
-@mock.patch.object(common_utils, 'execute', autospec=True)
-class GetDeviceBlockSizeTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(GetDeviceBlockSizeTestCase, self).setUp()
- self.dev = 'fake-dev'
- self.node_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
-
- def test_get_dev_block_size(self, mock_exec):
- mock_exec.return_value = ("64", "")
- expected_call = [mock.call('blockdev', '--getsz', self.dev,
- run_as_root=True, check_exit_code=[0])]
- utils.get_dev_block_size(self.dev)
- mock_exec.assert_has_calls(expected_call)
-
-
-@mock.patch.object(utils, 'dd', autospec=True)
-@mock.patch.object(images, 'qemu_img_info', autospec=True)
-@mock.patch.object(images, 'convert_image', autospec=True)
-class PopulateImageTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(PopulateImageTestCase, self).setUp()
-
- def test_populate_raw_image(self, mock_cg, mock_qinfo, mock_dd):
- type(mock_qinfo.return_value).file_format = mock.PropertyMock(
- return_value='raw')
- utils.populate_image('src', 'dst')
- mock_dd.assert_called_once_with('src', 'dst')
- self.assertFalse(mock_cg.called)
-
- def test_populate_qcow2_image(self, mock_cg, mock_qinfo, mock_dd):
- type(mock_qinfo.return_value).file_format = mock.PropertyMock(
- return_value='qcow2')
- utils.populate_image('src', 'dst')
- mock_cg.assert_called_once_with('src', 'dst', 'raw', True)
- self.assertFalse(mock_dd.called)
-
-
-@mock.patch.object(utils, 'is_block_device', lambda d: True)
-@mock.patch.object(utils, 'block_uuid', lambda p: 'uuid')
-@mock.patch.object(utils, 'dd', lambda *_: None)
-@mock.patch.object(images, 'convert_image', lambda *_: None)
-@mock.patch.object(common_utils, 'mkfs', lambda *_: None)
-# NOTE(dtantsur): destroy_disk_metadata resets file size, disabling it
-@mock.patch.object(utils, 'destroy_disk_metadata', lambda *_: None)
-class RealFilePartitioningTestCase(tests_base.TestCase):
- """This test applies some real-world partitioning scenario to a file.
-
- This test covers the whole partitioning, mocking everything not possible
- on a file. That helps us assure, that we do all partitioning math properly
- and also conducts integration testing of DiskPartitioner.
- """
-
- def setUp(self):
- super(RealFilePartitioningTestCase, self).setUp()
- # NOTE(dtantsur): no parted utility on gate-ironic-python26
- try:
- common_utils.execute('parted', '--version')
- except OSError as exc:
- self.skipTest('parted utility was not found: %s' % exc)
- self.file = tempfile.NamedTemporaryFile(delete=False)
- # NOTE(ifarkas): the file needs to be closed, so fuser won't report
- # any usage
- self.file.close()
- # NOTE(dtantsur): 20 MiB file with zeros
- common_utils.execute('dd', 'if=/dev/zero', 'of=%s' % self.file.name,
- 'bs=1', 'count=0', 'seek=20MiB')
-
- @staticmethod
- def _run_without_root(func, *args, **kwargs):
- """Make sure root is not required when using utils.execute."""
- real_execute = common_utils.execute
-
- def fake_execute(*cmd, **kwargs):
- kwargs['run_as_root'] = False
- return real_execute(*cmd, **kwargs)
-
- with mock.patch.object(common_utils, 'execute', fake_execute):
- return func(*args, **kwargs)
-
- def test_different_sizes(self):
- # NOTE(dtantsur): Keep this list in order with expected partitioning
- fields = ['ephemeral_mb', 'swap_mb', 'root_mb']
- variants = ((0, 0, 12), (4, 2, 8), (0, 4, 10), (5, 0, 10))
- for variant in variants:
- kwargs = dict(zip(fields, variant))
- self._run_without_root(utils.work_on_disk, self.file.name,
- ephemeral_format='ext4', node_uuid='',
- image_path='path', **kwargs)
- part_table = self._run_without_root(
- disk_partitioner.list_partitions, self.file.name)
- for part, expected_size in zip(part_table, filter(None, variant)):
- self.assertEqual(expected_size, part['size'],
- "comparison failed for %s" % list(variant))
-
- def test_whole_disk(self):
- # 6 MiB ephemeral + 3 MiB swap + 9 MiB root + 1 MiB for MBR
- # + 1 MiB MAGIC == 20 MiB whole disk
- # TODO(dtantsur): figure out why we need 'magic' 1 more MiB
- # and why the is different on Ubuntu and Fedora (see below)
- self._run_without_root(utils.work_on_disk, self.file.name,
- root_mb=9, ephemeral_mb=6, swap_mb=3,
- ephemeral_format='ext4', node_uuid='',
- image_path='path')
- part_table = self._run_without_root(
- disk_partitioner.list_partitions, self.file.name)
- sizes = [part['size'] for part in part_table]
- # NOTE(dtantsur): parted in Ubuntu 12.04 will occupy the last MiB,
- # parted in Fedora 20 won't - thus two possible variants for last part
- self.assertEqual([6, 3], sizes[:2],
- "unexpected partitioning %s" % part_table)
- self.assertIn(sizes[2], (9, 10))
-
@mock.patch.object(image_cache, 'clean_up_caches', autospec=True)
def test_fetch_images(self, mock_clean_up_caches):
@@ -1609,83 +1287,6 @@ class RealFilePartitioningTestCase(tests_base.TestCase):
[('uuid', 'path')])
-@mock.patch.object(tempfile, 'NamedTemporaryFile', autospec=True)
-@mock.patch.object(shutil, 'copyfileobj', autospec=True)
-@mock.patch.object(requests, 'get', autospec=True)
-class GetConfigdriveTestCase(tests_base.TestCase):
-
- def setUp(self):
- super(GetConfigdriveTestCase, self).setUp()
- # NOTE(lucasagomes): "name" can't be passed to Mock() when
- # instantiating the object because it's an expected parameter.
- # https://docs.python.org/3/library/unittest.mock.html
- self.fake_configdrive_file = mock.Mock(tell=lambda *_: 123)
- self.fake_configdrive_file.name = '/tmp/foo'
-
- @mock.patch.object(gzip, 'GzipFile', autospec=True)
- def test_get_configdrive(self, mock_gzip, mock_requests, mock_copy,
- mock_file):
- mock_file.return_value = self.fake_configdrive_file
- mock_requests.return_value = mock.MagicMock(
- spec_set=['content'], content='Zm9vYmFy')
- utils._get_configdrive('http://1.2.3.4/cd', 'fake-node-uuid')
- mock_requests.assert_called_once_with('http://1.2.3.4/cd')
- mock_gzip.assert_called_once_with('configdrive', 'rb',
- fileobj=mock.ANY)
- mock_copy.assert_called_once_with(mock.ANY, mock.ANY)
- mock_file.assert_called_once_with(prefix='configdrive',
- dir=cfg.CONF.tempdir, delete=False)
-
- @mock.patch.object(gzip, 'GzipFile', autospec=True)
- def test_get_configdrive_base64_string(self, mock_gzip, mock_requests,
- mock_copy, mock_file):
- mock_file.return_value = self.fake_configdrive_file
- utils._get_configdrive('Zm9vYmFy', 'fake-node-uuid')
- self.assertFalse(mock_requests.called)
- mock_gzip.assert_called_once_with('configdrive', 'rb',
- fileobj=mock.ANY)
- mock_copy.assert_called_once_with(mock.ANY, mock.ANY)
- mock_file.assert_called_once_with(prefix='configdrive',
- dir=cfg.CONF.tempdir, delete=False)
-
- def test_get_configdrive_bad_url(self, mock_requests, mock_copy,
- mock_file):
- mock_requests.side_effect = requests.exceptions.RequestException
- self.assertRaises(exception.InstanceDeployFailure,
- utils._get_configdrive, 'http://1.2.3.4/cd',
- 'fake-node-uuid')
- self.assertFalse(mock_copy.called)
- self.assertFalse(mock_file.called)
-
- @mock.patch.object(base64, 'b64decode', autospec=True)
- def test_get_configdrive_base64_error(self, mock_b64, mock_requests,
- mock_copy, mock_file):
- mock_b64.side_effect = TypeError
- self.assertRaises(exception.InstanceDeployFailure,
- utils._get_configdrive,
- 'malformed', 'fake-node-uuid')
- mock_b64.assert_called_once_with('malformed')
- self.assertFalse(mock_copy.called)
- self.assertFalse(mock_file.called)
-
- @mock.patch.object(gzip, 'GzipFile', autospec=True)
- def test_get_configdrive_gzip_error(self, mock_gzip, mock_requests,
- mock_copy, mock_file):
- mock_file.return_value = self.fake_configdrive_file
- mock_requests.return_value = mock.MagicMock(
- spec_set=['content'], content='Zm9vYmFy')
- mock_copy.side_effect = IOError
- self.assertRaises(exception.InstanceDeployFailure,
- utils._get_configdrive, 'http://1.2.3.4/cd',
- 'fake-node-uuid')
- mock_requests.assert_called_once_with('http://1.2.3.4/cd')
- mock_gzip.assert_called_once_with('configdrive', 'rb',
- fileobj=mock.ANY)
- mock_copy.assert_called_once_with(mock.ANY, mock.ANY)
- mock_file.assert_called_once_with(prefix='configdrive',
- dir=cfg.CONF.tempdir, delete=False)
-
-
class VirtualMediaDeployUtilsTestCase(db_base.DbTestCase):
def setUp(self):
@@ -2217,7 +1818,7 @@ class AgentMethodsTestCase(db_base.DbTestCase):
self.assertEqual('model=fake_model', options['root_device'])
-@mock.patch.object(utils, 'is_block_device', autospec=True)
+@mock.patch.object(disk_utils, 'is_block_device', autospec=True)
@mock.patch.object(utils, 'login_iscsi', lambda *_: None)
@mock.patch.object(utils, 'discovery', lambda *_: None)
@mock.patch.object(utils, 'logout_iscsi', lambda *_: None)
diff --git a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py
index 5fea6de75..01f396b65 100644
--- a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py
+++ b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py
@@ -18,6 +18,7 @@
import os
import tempfile
+from ironic_lib import disk_utils
import mock
from oslo_config import cfg
from oslo_utils import fileutils
@@ -344,7 +345,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
self.node = obj_utils.create_test_node(self.context, **n)
- @mock.patch.object(deploy_utils, 'get_image_mb', autospec=True)
+ @mock.patch.object(disk_utils, 'get_image_mb', autospec=True)
def test_check_image_size(self, get_image_mb_mock):
get_image_mb_mock.return_value = 1000
with task_manager.acquire(self.context, self.node.uuid,
@@ -354,7 +355,7 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
get_image_mb_mock.assert_called_once_with(
iscsi_deploy._get_image_file_path(task.node.uuid))
- @mock.patch.object(deploy_utils, 'get_image_mb', autospec=True)
+ @mock.patch.object(disk_utils, 'get_image_mb', autospec=True)
def test_check_image_size_fails(self, get_image_mb_mock):
get_image_mb_mock.return_value = 1025
with task_manager.acquire(self.context, self.node.uuid,
diff --git a/releasenotes/notes/refactor-ironic-lib-22939896d8d46a77.yaml b/releasenotes/notes/refactor-ironic-lib-22939896d8d46a77.yaml
new file mode 100644
index 000000000..19d76938f
--- /dev/null
+++ b/releasenotes/notes/refactor-ironic-lib-22939896d8d46a77.yaml
@@ -0,0 +1,20 @@
+---
+upgrade:
+ - |
+ Adds new configuration [ironic_lib]root_helper, to specify
+ the command that is prefixed to commands that are run as root.
+ Defaults to using the rootwrap config file at
+ /etc/ironic/rootwrap.conf.
+ - |
+ Moves these configuration options from [deploy] group to the
+ new [disk_utils] group: efi_system_partition_size, dd_block_size
+ and iscsi_verify_attempts.
+deprecations:
+ - |
+ The following configuration options have been moved to
+ the [disk_utils] group; they are deprecated from the
+ [deploy] group: efi_system_partition_size, dd_block_size and
+ iscsi_verify_attempts.
+other:
+ - Code related to disk partitioning was moved to
+ ironic-lib.
diff --git a/requirements.txt b/requirements.txt
index 98a236c47..65890407a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,6 +13,7 @@ paramiko>=1.13.0
python-neutronclient>=2.6.0
python-glanceclient>=0.18.0
python-keystoneclient!=1.8.0,>=1.6.0
+ironic-lib>=0.5.0
python-swiftclient>=2.2.0
pytz>=2013.6
stevedore>=1.5.0 # Apache-2.0
diff --git a/tools/config/oslo.config.generator.rc b/tools/config/oslo.config.generator.rc
index e66acd5ac..447d8144d 100644
--- a/tools/config/oslo.config.generator.rc
+++ b/tools/config/oslo.config.generator.rc
@@ -1,2 +1,2 @@
export IRONIC_CONFIG_GENERATOR_EXTRA_LIBRARIES='oslo.db oslo.messaging oslo.middleware.cors keystonemiddleware.auth_token oslo.concurrency oslo.policy oslo.log oslo.service.service oslo.service.periodic_task oslo.service.sslutils'
-export IRONIC_CONFIG_GENERATOR_EXTRA_MODULES=
+export IRONIC_CONFIG_GENERATOR_EXTRA_MODULES='ironic_lib.disk_utils ironic_lib.disk_partitioner ironic_lib.utils'