Remove support for the old bash ramdisk
From now on only rely on the IPA inventory and 2 additional fields: boot_interface and root_device. Also updated unit tests to use one inventory example. Also removed duplicating unit tests and checks in test_process. Also removed devstack support for the old ramdisk. Change-Id: Ib382328295fc2c1b9143171b1047304febadcaca
This commit is contained in:
parent
f66592b4f0
commit
0b58e31e3e
@ -86,9 +86,6 @@ components. There is a plugin for installing **ironic-inspector** on DevStack.
|
||||
Example local.conf
|
||||
------------------
|
||||
|
||||
Using IPA
|
||||
~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
[[local|localrc]]
|
||||
@ -108,7 +105,6 @@ Using IPA
|
||||
|
||||
IRONIC_DEPLOY_DRIVER_ISCSI_WITH_IPA=True
|
||||
IRONIC_BUILD_DEPLOY_RAMDISK=False
|
||||
IRONIC_INSPECTOR_RAMDISK_ELEMENT=ironic-agent
|
||||
IRONIC_INSPECTOR_BUILD_RAMDISK=False
|
||||
|
||||
VIRT_DRIVER=ironic
|
||||
@ -136,52 +132,6 @@ Notes
|
||||
* This configuration disables Heat and Cinder, adjust it if you need these
|
||||
services.
|
||||
|
||||
Using simple ramdisk
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
This ramdisk is deprecated and should not be used.
|
||||
|
||||
::
|
||||
|
||||
[[local|localrc]]
|
||||
enable_service ironic ir-api ir-cond
|
||||
disable_service n-net n-novnc
|
||||
enable_service neutron q-svc q-agt q-dhcp q-l3 q-meta
|
||||
enable_service s-proxy s-object s-container s-account
|
||||
disable_service heat h-api h-api-cfn h-api-cw h-eng
|
||||
disable_service cinder c-sch c-api c-vol
|
||||
|
||||
enable_plugin ironic https://github.com/openstack/ironic
|
||||
enable_plugin ironic-inspector https://github.com/openstack/ironic-inspector
|
||||
|
||||
IRONIC_BAREMETAL_BASIC_OPS=True
|
||||
IRONIC_VM_COUNT=2
|
||||
IRONIC_VM_SPECS_RAM=1024
|
||||
IRONIC_DEPLOY_FLAVOR="fedora deploy-ironic"
|
||||
|
||||
IRONIC_INSPECTOR_RAMDISK_FLAVOR="fedora ironic-discoverd-ramdisk"
|
||||
|
||||
VIRT_DRIVER=ironic
|
||||
|
||||
LOGDAYS=1
|
||||
LOGFILE=~/logs/stack.sh.log
|
||||
SCREEN_LOGDIR=~/logs/screen
|
||||
|
||||
DEFAULT_INSTANCE_TYPE=baremetal
|
||||
TEMPEST_ALLOW_TENANT_ISOLATION=False
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* Replace "fedora" with whatever you have
|
||||
|
||||
* You need at least 1G of RAM for VMs, default value of 512 MB won't work
|
||||
|
||||
* Before restarting stack.sh::
|
||||
|
||||
rm -rf /opt/stack/ironic-inspector
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
|
@ -18,8 +18,6 @@ IRONIC_INSPECTOR_URI="http://$IRONIC_INSPECTOR_HOST:$IRONIC_INSPECTOR_PORT"
|
||||
IRONIC_INSPECTOR_BUILD_RAMDISK=$(trueorfalse False IRONIC_INSPECTOR_BUILD_RAMDISK)
|
||||
IRONIC_AGENT_KERNEL_URL=${IRONIC_AGENT_KERNEL_URL:-http://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe.vmlinuz}
|
||||
IRONIC_AGENT_RAMDISK_URL=${IRONIC_AGENT_RAMDISK_URL:-http://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe_image-oem.cpio.gz}
|
||||
IRONIC_INSPECTOR_RAMDISK_ELEMENT=${IRONIC_INSPECTOR_RAMDISK_ELEMENT:-ironic-discoverd-ramdisk}
|
||||
IRONIC_INSPECTOR_RAMDISK_FLAVOR=${IRONIC_INSPECTOR_RAMDISK_FLAVOR:-fedora $IRONIC_INSPECTOR_RAMDISK_ELEMENT}
|
||||
IRONIC_INSPECTOR_COLLECTORS=${IRONIC_INSPECTOR_COLLECTORS:-default,logs}
|
||||
IRONIC_INSPECTOR_RAMDISK_LOGDIR=${IRONIC_INSPECTOR_RAMDISK_LOGDIR:-$IRONIC_INSPECTOR_DATA_DIR/ramdisk-logs}
|
||||
IRONIC_INSPECTOR_ALWAYS_STORE_RAMDISK_LOGS=${IRONIC_INSPECTOR_ALWAYS_STORE_RAMDISK_LOGS:-True}
|
||||
@ -91,11 +89,6 @@ function stop_inspector_dhcp {
|
||||
screen -S $SCREEN_NAME -p ironic-inspector-dhcp -X kill
|
||||
}
|
||||
|
||||
function inspector_uses_ipa {
|
||||
[[ $IRONIC_INSPECTOR_RAMDISK_ELEMENT = "ironic-agent" ]] || [[ $IRONIC_INSPECTOR_RAMDISK_FLAVOR =~ (ironic-agent$|^ironic-agent) ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
### Configuration
|
||||
|
||||
function prepare_tftp {
|
||||
@ -104,35 +97,24 @@ function prepare_tftp {
|
||||
IRONIC_INSPECTOR_INITRAMFS_PATH="$IRONIC_INSPECTOR_IMAGE_PATH.initramfs"
|
||||
IRONIC_INSPECTOR_CALLBACK_URI="$IRONIC_INSPECTOR_INTERNAL_URI/v1/continue"
|
||||
|
||||
if inspector_uses_ipa; then
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="ipa-inspection-callback-url=$IRONIC_INSPECTOR_CALLBACK_URI systemd.journald.forward_to_console=yes"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE vga=normal console=tty0 console=ttyS0"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE ipa-inspection-collectors=$IRONIC_INSPECTOR_COLLECTORS"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE ipa-debug=1"
|
||||
if [[ "$IRONIC_INSPECTOR_BUILD_RAMDISK" == "True" ]]; then
|
||||
if [ ! -e "$IRONIC_INSPECTOR_KERNEL_PATH" -o ! -e "$IRONIC_INSPECTOR_INITRAMFS_PATH" ]; then
|
||||
build_ipa_coreos_ramdisk "$IRONIC_INSPECTOR_KERNEL_PATH" "$IRONIC_INSPECTOR_INITRAMFS_PATH"
|
||||
fi
|
||||
else
|
||||
# download the agent image tarball
|
||||
if [ ! -e "$IRONIC_INSPECTOR_KERNEL_PATH" -o ! -e "$IRONIC_INSPECTOR_INITRAMFS_PATH" ]; then
|
||||
if [ -e "$IRONIC_DEPLOY_KERNEL_PATH" -a -e "$IRONIC_DEPLOY_RAMDISK_PATH" ]; then
|
||||
cp $IRONIC_DEPLOY_KERNEL_PATH $IRONIC_INSPECTOR_KERNEL_PATH
|
||||
cp $IRONIC_DEPLOY_RAMDISK_PATH $IRONIC_INSPECTOR_INITRAMFS_PATH
|
||||
else
|
||||
wget "$IRONIC_AGENT_KERNEL_URL" -O $IRONIC_INSPECTOR_KERNEL_PATH
|
||||
wget "$IRONIC_AGENT_RAMDISK_URL" -O $IRONIC_INSPECTOR_INITRAMFS_PATH
|
||||
fi
|
||||
fi
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="ipa-inspection-callback-url=$IRONIC_INSPECTOR_CALLBACK_URI systemd.journald.forward_to_console=yes"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE vga=normal console=tty0 console=ttyS0"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE ipa-inspection-collectors=$IRONIC_INSPECTOR_COLLECTORS"
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="$IRONIC_INSPECTOR_KERNEL_CMDLINE ipa-debug=1"
|
||||
if [[ "$IRONIC_INSPECTOR_BUILD_RAMDISK" == "True" ]]; then
|
||||
if [ ! -e "$IRONIC_INSPECTOR_KERNEL_PATH" -o ! -e "$IRONIC_INSPECTOR_INITRAMFS_PATH" ]; then
|
||||
build_ipa_coreos_ramdisk "$IRONIC_INSPECTOR_KERNEL_PATH" "$IRONIC_INSPECTOR_INITRAMFS_PATH"
|
||||
fi
|
||||
else
|
||||
IRONIC_INSPECTOR_KERNEL_CMDLINE="discoverd_callback_url=$IRONIC_INSPECTOR_CALLBACK_URI inspector_callback_url=$IRONIC_INSPECTOR_CALLBACK_URI"
|
||||
# download the agent image tarball
|
||||
if [ ! -e "$IRONIC_INSPECTOR_KERNEL_PATH" -o ! -e "$IRONIC_INSPECTOR_INITRAMFS_PATH" ]; then
|
||||
if [[ $(type -P ramdisk-image-create) == "" ]]; then
|
||||
pip_install diskimage_builder
|
||||
if [ -e "$IRONIC_DEPLOY_KERNEL_PATH" -a -e "$IRONIC_DEPLOY_RAMDISK_PATH" ]; then
|
||||
cp $IRONIC_DEPLOY_KERNEL_PATH $IRONIC_INSPECTOR_KERNEL_PATH
|
||||
cp $IRONIC_DEPLOY_RAMDISK_PATH $IRONIC_INSPECTOR_INITRAMFS_PATH
|
||||
else
|
||||
wget "$IRONIC_AGENT_KERNEL_URL" -O $IRONIC_INSPECTOR_KERNEL_PATH
|
||||
wget "$IRONIC_AGENT_RAMDISK_URL" -O $IRONIC_INSPECTOR_INITRAMFS_PATH
|
||||
fi
|
||||
ramdisk-image-create $IRONIC_INSPECTOR_RAMDISK_FLAVOR \
|
||||
-o $IRONIC_INSPECTOR_IMAGE_PATH
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -217,22 +217,6 @@ Optionally the following keys might be provided:
|
||||
|
||||
* ``logs`` base64-encoded logs from the ramdisk.
|
||||
|
||||
The following keys are supported for backward compatibility with the old
|
||||
bash-based ramdisk, when ``inventory`` is not provided:
|
||||
|
||||
* ``cpus`` number of CPU
|
||||
|
||||
* ``cpu_arch`` architecture of the CPU
|
||||
|
||||
* ``memory_mb`` RAM in MiB
|
||||
|
||||
* ``local_gb`` hard drive size in GiB
|
||||
|
||||
* ``ipmi_address`` IP address of BMC, may be missing on VM
|
||||
|
||||
* ``block_devices`` block devices information for the ``raid_device`` plugin,
|
||||
dictionary with one key: ``serials`` list of serial numbers of block devices.
|
||||
|
||||
.. note::
|
||||
This list highly depends on enabled plugins, provided above are
|
||||
expected keys for the default set of plugins. See :ref:`plugins`
|
||||
|
@ -132,9 +132,8 @@ As for PXE boot environment, you'll need:
|
||||
simultaneously cause conflicts - the same IP address is suggested to
|
||||
several nodes.
|
||||
|
||||
* You have to install and configure one of 2 available ramdisks: simple
|
||||
bash-based (see `Using simple ramdisk`_) or more complex based on
|
||||
ironic-python-agent_ (See `Using IPA`_).
|
||||
* You have to install and configure the ramdisk to be run on target machines -
|
||||
see `Configuring IPA`_.
|
||||
|
||||
Here is *inspector.conf* you may end up with::
|
||||
|
||||
@ -152,8 +151,8 @@ Here is *inspector.conf* you may end up with::
|
||||
.. note::
|
||||
Set ``debug = true`` if you want to see complete logs.
|
||||
|
||||
Using IPA
|
||||
^^^^^^^^^
|
||||
Configuring IPA
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
ironic-python-agent_ is a ramdisk developed for Ironic. During the Liberty
|
||||
cycle support for **ironic-inspector** was added. This is the default ramdisk
|
||||
@ -215,34 +214,6 @@ This ramdisk is pluggable: you can insert introspection plugins called
|
||||
.. _diskimage-builder: https://github.com/openstack/diskimage-builder
|
||||
.. _ironic-python-agent: https://github.com/openstack/ironic-python-agent
|
||||
|
||||
Using simple ramdisk
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This ramdisk is deprecated, its use is not recommended.
|
||||
|
||||
* Build and put into your TFTP the kernel and ramdisk created using the
|
||||
diskimage-builder_ `ironic-discoverd-ramdisk element`_::
|
||||
|
||||
ramdisk-image-create -o discovery fedora ironic-discoverd-ramdisk
|
||||
|
||||
You need diskimage-builder_ 0.1.38 or newer to do it (using the latest one
|
||||
is always advised).
|
||||
|
||||
* Configure your ``$TFTPROOT/pxelinux.cfg/default`` with something like::
|
||||
|
||||
default introspect
|
||||
|
||||
label introspect
|
||||
kernel discovery.kernel
|
||||
append initrd=discovery.initramfs discoverd_callback_url=http://{IP}:5050/v1/continue
|
||||
|
||||
ipappend 3
|
||||
|
||||
Replace ``{IP}`` with IP of the machine (do not use loopback interface, it
|
||||
will be accessed by ramdisk on a booting machine).
|
||||
|
||||
.. _ironic-discoverd-ramdisk element: https://github.com/openstack/diskimage-builder/tree/master/elements/ironic-discoverd-ramdisk
|
||||
|
||||
Managing the **ironic-inspector** database
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -46,13 +46,6 @@ class RootDiskSelectionHook(base.ProcessingHook):
|
||||
node_info=node_info, data=introspection_data)
|
||||
return
|
||||
|
||||
inventory = introspection_data.get('inventory')
|
||||
if not inventory:
|
||||
raise utils.Error(
|
||||
_('Root device selection requires ironic-python-agent '
|
||||
'as an inspection ramdisk'),
|
||||
node_info=node_info, data=introspection_data)
|
||||
|
||||
if 'size' in hints:
|
||||
# Special case to match IPA behaviour
|
||||
try:
|
||||
@ -62,12 +55,9 @@ class RootDiskSelectionHook(base.ProcessingHook):
|
||||
'an integer, got %s') % hints['size'],
|
||||
node_info=node_info, data=introspection_data)
|
||||
|
||||
disks = inventory.get('disks', [])
|
||||
if not disks:
|
||||
raise utils.Error(_('No disks found'),
|
||||
node_info=node_info, data=introspection_data)
|
||||
|
||||
for disk in disks:
|
||||
inventory = utils.get_inventory(introspection_data,
|
||||
node_info=node_info)
|
||||
for disk in inventory['disks']:
|
||||
properties = disk.copy()
|
||||
# Root device hints are in GiB, data from IPA is in bytes
|
||||
properties['size'] //= units.Gi
|
||||
@ -100,7 +90,8 @@ class SchedulerHook(base.ProcessingHook):
|
||||
|
||||
def before_update(self, introspection_data, node_info, **kwargs):
|
||||
"""Update node with scheduler properties."""
|
||||
inventory = introspection_data.get('inventory')
|
||||
inventory = utils.get_inventory(introspection_data,
|
||||
node_info=node_info)
|
||||
errors = []
|
||||
|
||||
root_disk = introspection_data.get('root_disk')
|
||||
@ -108,40 +99,25 @@ class SchedulerHook(base.ProcessingHook):
|
||||
introspection_data['local_gb'] = root_disk['size'] // units.Gi
|
||||
if CONF.processing.disk_partitioning_spacing:
|
||||
introspection_data['local_gb'] -= 1
|
||||
elif inventory:
|
||||
else:
|
||||
errors.append(_('root disk is not supplied by the ramdisk and '
|
||||
'root_disk_selection hook is not enabled'))
|
||||
|
||||
if inventory:
|
||||
try:
|
||||
introspection_data['cpus'] = int(inventory['cpu']['count'])
|
||||
introspection_data['cpu_arch'] = six.text_type(
|
||||
inventory['cpu']['architecture'])
|
||||
except (KeyError, ValueError, TypeError):
|
||||
errors.append(_('malformed or missing CPU information: %s') %
|
||||
inventory.get('cpu'))
|
||||
try:
|
||||
introspection_data['cpus'] = int(inventory['cpu']['count'])
|
||||
introspection_data['cpu_arch'] = six.text_type(
|
||||
inventory['cpu']['architecture'])
|
||||
except (KeyError, ValueError, TypeError):
|
||||
errors.append(_('malformed or missing CPU information: %s') %
|
||||
inventory.get('cpu'))
|
||||
|
||||
try:
|
||||
introspection_data['memory_mb'] = int(
|
||||
inventory['memory']['physical_mb'])
|
||||
except (KeyError, ValueError, TypeError):
|
||||
errors.append(_('malformed or missing memory information: %s; '
|
||||
'introspection requires physical memory size '
|
||||
'from dmidecode') %
|
||||
inventory.get('memory'))
|
||||
else:
|
||||
LOG.warning(_LW('No inventory provided: using old bash ramdisk '
|
||||
'is deprecated, please switch to '
|
||||
'ironic-python-agent'),
|
||||
node_info=node_info, data=introspection_data)
|
||||
|
||||
missing = [key for key in self.KEYS
|
||||
if not introspection_data.get(key)]
|
||||
if missing:
|
||||
raise utils.Error(
|
||||
_('The following required parameters are missing: %s') %
|
||||
missing,
|
||||
node_info=node_info, data=introspection_data)
|
||||
try:
|
||||
introspection_data['memory_mb'] = int(
|
||||
inventory['memory']['physical_mb'])
|
||||
except (KeyError, ValueError, TypeError):
|
||||
errors.append(_('malformed or missing memory information: %s; '
|
||||
'introspection requires physical memory size '
|
||||
'from dmidecode') % inventory.get('memory'))
|
||||
|
||||
if errors:
|
||||
raise utils.Error(_('The following problems encountered: %s') %
|
||||
@ -184,42 +160,36 @@ class ValidateInterfacesHook(base.ProcessingHook):
|
||||
:return: dict interface name -> dict with keys 'mac' and 'ip'
|
||||
"""
|
||||
result = {}
|
||||
inventory = data.get('inventory', {})
|
||||
inventory = utils.get_inventory(data)
|
||||
|
||||
if inventory:
|
||||
for iface in inventory.get('interfaces', ()):
|
||||
name = iface.get('name')
|
||||
mac = iface.get('mac_address')
|
||||
ip = iface.get('ipv4_address')
|
||||
for iface in inventory['interfaces']:
|
||||
name = iface.get('name')
|
||||
mac = iface.get('mac_address')
|
||||
ip = iface.get('ipv4_address')
|
||||
|
||||
if not name:
|
||||
LOG.error(_LE('Malformed interface record: %s'),
|
||||
iface, data=data)
|
||||
continue
|
||||
if not name:
|
||||
LOG.error(_LE('Malformed interface record: %s'),
|
||||
iface, data=data)
|
||||
continue
|
||||
|
||||
if not mac:
|
||||
LOG.debug('Skipping interface %s without link information',
|
||||
name, data=data)
|
||||
continue
|
||||
if not mac:
|
||||
LOG.debug('Skipping interface %s without link information',
|
||||
name, data=data)
|
||||
continue
|
||||
|
||||
if not utils.is_valid_mac(mac):
|
||||
LOG.warning(_LW('MAC %(mac)s for interface %(name)s is '
|
||||
'not valid, skipping'),
|
||||
{'mac': mac, 'name': name},
|
||||
data=data)
|
||||
continue
|
||||
if not utils.is_valid_mac(mac):
|
||||
LOG.warning(_LW('MAC %(mac)s for interface %(name)s is '
|
||||
'not valid, skipping'),
|
||||
{'mac': mac, 'name': name},
|
||||
data=data)
|
||||
continue
|
||||
|
||||
mac = mac.lower()
|
||||
mac = mac.lower()
|
||||
|
||||
LOG.debug('Found interface %(name)s with MAC "%(mac)s" and '
|
||||
'IP address "%(ip)s"',
|
||||
{'name': name, 'mac': mac, 'ip': ip}, data=data)
|
||||
result[name] = {'ip': ip, 'mac': mac}
|
||||
else:
|
||||
LOG.warning(_LW('No inventory provided: using old bash ramdisk '
|
||||
'is deprecated, please switch to '
|
||||
'ironic-python-agent'), data=data)
|
||||
result = data.get('interfaces')
|
||||
LOG.debug('Found interface %(name)s with MAC "%(mac)s" and '
|
||||
'IP address "%(ip)s"',
|
||||
{'name': name, 'mac': mac, 'ip': ip}, data=data)
|
||||
result[name] = {'ip': ip, 'mac': mac}
|
||||
|
||||
return result
|
||||
|
||||
|
@ -286,10 +286,10 @@ def _finish_set_ipmi_credentials(ironic, node, node_info, introspection_data,
|
||||
'value': new_username},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': new_password}]
|
||||
if (not ir_utils.get_ipmi_address(node) and
|
||||
introspection_data.get('ipmi_address')):
|
||||
new_ipmi_address = utils.get_ipmi_address_from_data(introspection_data)
|
||||
if not ir_utils.get_ipmi_address(node) and new_ipmi_address:
|
||||
patch.append({'op': 'add', 'path': '/driver_info/ipmi_address',
|
||||
'value': introspection_data['ipmi_address']})
|
||||
'value': new_ipmi_address})
|
||||
node_info.patch(patch)
|
||||
|
||||
for attempt in range(_CREDENTIALS_WAIT_RETRIES):
|
||||
|
@ -19,6 +19,7 @@ from oslo_concurrency import lockutils
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslo_log import log
|
||||
from oslo_utils import units
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from ironic_inspector.common import i18n
|
||||
@ -78,12 +79,66 @@ class BaseTest(fixtures.TestWithFixtures):
|
||||
self.assertPatchEqual(actual, expected)
|
||||
|
||||
|
||||
class NodeTest(BaseTest):
|
||||
class InventoryTest(BaseTest):
|
||||
def setUp(self):
|
||||
super(InventoryTest, self).setUp()
|
||||
# Prepare some realistic inventory
|
||||
# https://github.com/openstack/ironic-inspector/blob/master/HTTP-API.rst # noqa
|
||||
self.bmc_address = '1.2.3.4'
|
||||
self.macs = ['11:22:33:44:55:66', '66:55:44:33:22:11']
|
||||
self.ips = ['1.2.1.2', '1.2.1.1']
|
||||
self.inactive_mac = '12:12:21:12:21:12'
|
||||
self.pxe_mac = self.macs[0]
|
||||
self.all_macs = self.macs + [self.inactive_mac]
|
||||
self.pxe_iface_name = 'eth1'
|
||||
self.data = {
|
||||
'boot_interface': '01-' + self.pxe_mac.replace(':', '-'),
|
||||
'inventory': {
|
||||
'interfaces': [
|
||||
{'name': 'eth1', 'mac_address': self.macs[0],
|
||||
'ipv4_address': self.ips[0]},
|
||||
{'name': 'eth2', 'mac_address': self.inactive_mac},
|
||||
{'name': 'eth3', 'mac_address': self.macs[1],
|
||||
'ipv4_address': self.ips[1]},
|
||||
],
|
||||
'disks': [
|
||||
{'name': '/dev/sda', 'model': 'Big Data Disk',
|
||||
'size': 1000 * units.Gi},
|
||||
{'name': '/dev/sdb', 'model': 'Small OS Disk',
|
||||
'size': 20 * units.Gi},
|
||||
],
|
||||
'cpu': {
|
||||
'count': 4,
|
||||
'architecture': 'x86_64'
|
||||
},
|
||||
'memory': {
|
||||
'physical_mb': 12288
|
||||
},
|
||||
'bmc_address': self.bmc_address
|
||||
},
|
||||
'root_disk': {'name': '/dev/sda', 'model': 'Big Data Disk',
|
||||
'size': 1000 * units.Gi,
|
||||
'wwn': None},
|
||||
}
|
||||
self.inventory = self.data['inventory']
|
||||
self.all_interfaces = {
|
||||
'eth1': {'mac': self.macs[0], 'ip': self.ips[0]},
|
||||
'eth2': {'mac': self.inactive_mac, 'ip': None},
|
||||
'eth3': {'mac': self.macs[1], 'ip': self.ips[1]}
|
||||
}
|
||||
self.active_interfaces = {
|
||||
'eth1': {'mac': self.macs[0], 'ip': self.ips[0]},
|
||||
'eth3': {'mac': self.macs[1], 'ip': self.ips[1]}
|
||||
}
|
||||
self.pxe_interfaces = {
|
||||
self.pxe_iface_name: self.all_interfaces[self.pxe_iface_name]
|
||||
}
|
||||
|
||||
|
||||
class NodeTest(InventoryTest):
|
||||
def setUp(self):
|
||||
super(NodeTest, self).setUp()
|
||||
self.uuid = uuidutils.generate_uuid()
|
||||
self.bmc_address = '1.2.3.4'
|
||||
self.macs = ['11:22:33:44:55:66', '66:55:44:33:22:11']
|
||||
fake_node = {
|
||||
'driver': 'pxe_ipmitool',
|
||||
'driver_info': {'ipmi_address': self.bmc_address},
|
||||
|
@ -24,7 +24,6 @@ import unittest
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import units
|
||||
import requests
|
||||
|
||||
from ironic_inspector.common import ironic as ir_utils
|
||||
@ -70,62 +69,12 @@ class Base(base.NodeTest):
|
||||
self.cli.node.update.return_value = self.node
|
||||
self.cli.node.list.return_value = [self.node]
|
||||
|
||||
# https://github.com/openstack/ironic-inspector/blob/master/HTTP-API.rst # noqa
|
||||
self.data = {
|
||||
'boot_interface': '01-' + self.macs[0].replace(':', '-'),
|
||||
'inventory': {
|
||||
'interfaces': [
|
||||
{'name': 'eth1', 'mac_address': self.macs[0],
|
||||
'ipv4_address': '1.2.1.2'},
|
||||
{'name': 'eth2', 'mac_address': '12:12:21:12:21:12'},
|
||||
{'name': 'eth3', 'mac_address': self.macs[1],
|
||||
'ipv4_address': '1.2.1.1'},
|
||||
],
|
||||
'disks': [
|
||||
{'name': '/dev/sda', 'model': 'Big Data Disk',
|
||||
'size': 1000 * units.Gi},
|
||||
{'name': '/dev/sdb', 'model': 'Small OS Disk',
|
||||
'size': 20 * units.Gi},
|
||||
],
|
||||
'cpu': {
|
||||
'count': 4,
|
||||
'architecture': 'x86_64'
|
||||
},
|
||||
'memory': {
|
||||
'physical_mb': 12288
|
||||
},
|
||||
'bmc_address': self.bmc_address
|
||||
},
|
||||
'root_disk': {'name': '/dev/sda', 'model': 'Big Data Disk',
|
||||
'size': 1000 * units.Gi,
|
||||
'wwn': None},
|
||||
}
|
||||
self.data_old_ramdisk = {
|
||||
'cpus': 4,
|
||||
'cpu_arch': 'x86_64',
|
||||
'memory_mb': 12288,
|
||||
'local_gb': 464,
|
||||
'interfaces': {
|
||||
'eth1': {'mac': self.macs[0], 'ip': '1.2.1.2'},
|
||||
'eth2': {'mac': '12:12:21:12:21:12'},
|
||||
'eth3': {'mac': self.macs[1], 'ip': '1.2.1.1'},
|
||||
},
|
||||
'boot_interface': '01-' + self.macs[0].replace(':', '-'),
|
||||
'ipmi_address': self.bmc_address,
|
||||
}
|
||||
|
||||
self.patch = [
|
||||
{'op': 'add', 'path': '/properties/cpus', 'value': '4'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'op': 'add', 'path': '/properties/memory_mb', 'value': '12288'},
|
||||
{'path': '/properties/local_gb', 'value': '999', 'op': 'add'}
|
||||
]
|
||||
self.patch_old_ramdisk = [
|
||||
{'op': 'add', 'path': '/properties/cpus', 'value': '4'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'op': 'add', 'path': '/properties/memory_mb', 'value': '12288'},
|
||||
{'path': '/properties/local_gb', 'value': '464', 'op': 'add'}
|
||||
]
|
||||
self.patch_root_hints = [
|
||||
{'op': 'add', 'path': '/properties/cpus', 'value': '4'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
@ -211,27 +160,6 @@ class Test(Base):
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.assertEqual({'finished': True, 'error': None}, status)
|
||||
|
||||
def test_old_ramdisk(self):
|
||||
self.call_introspect(self.uuid)
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||
'reboot')
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.assertEqual({'finished': False, 'error': None}, status)
|
||||
|
||||
res = self.call_continue(self.data_old_ramdisk)
|
||||
self.assertEqual({'uuid': self.uuid}, res)
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
|
||||
self.assertCalledWithPatch(self.patch_old_ramdisk,
|
||||
self.cli.node.update)
|
||||
self.cli.port.create.assert_called_once_with(
|
||||
node_uuid=self.uuid, address='11:22:33:44:55:66')
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.assertEqual({'finished': True, 'error': None}, status)
|
||||
|
||||
def test_setup_ipmi(self):
|
||||
patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
|
@ -29,22 +29,6 @@ class TestSchedulerHook(test_base.NodeTest):
|
||||
def setUp(self):
|
||||
super(TestSchedulerHook, self).setUp()
|
||||
self.hook = std_plugins.SchedulerHook()
|
||||
self.data = {
|
||||
'inventory': {
|
||||
'cpu': {'count': 2, 'architecture': 'x86_64'},
|
||||
'memory': {'physical_mb': 1024},
|
||||
},
|
||||
'root_disk': {
|
||||
'name': '/dev/sda',
|
||||
'size': 21 * units.Gi
|
||||
}
|
||||
}
|
||||
self.old_data = {
|
||||
'local_gb': 20,
|
||||
'memory_mb': 1024,
|
||||
'cpus': 2,
|
||||
'cpu_arch': 'x86_64'
|
||||
}
|
||||
self.node_info = node_cache.NodeInfo(uuid=self.uuid, started_at=0,
|
||||
node=self.node)
|
||||
|
||||
@ -53,43 +37,24 @@ class TestSchedulerHook(test_base.NodeTest):
|
||||
ext = base.processing_hooks_manager()['scheduler']
|
||||
self.assertIsInstance(ext.obj, std_plugins.SchedulerHook)
|
||||
|
||||
def test_compat_missing(self):
|
||||
for key in self.old_data:
|
||||
new_data = self.old_data.copy()
|
||||
del new_data[key]
|
||||
self.assertRaisesRegexp(utils.Error, key,
|
||||
self.hook.before_update, new_data,
|
||||
self.node_info)
|
||||
|
||||
def test_no_root_disk(self):
|
||||
self.assertRaisesRegexp(utils.Error, 'root disk is not supplied',
|
||||
self.hook.before_update,
|
||||
{'inventory': {'disks': []}}, self.node_info)
|
||||
del self.inventory['disks']
|
||||
self.assertRaisesRegexp(utils.Error, 'disks key is missing or empty',
|
||||
self.hook.before_update, self.data,
|
||||
self.node_info)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch')
|
||||
def test_ok(self, mock_patch):
|
||||
patch = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/cpus', 'value': '4', 'op': 'add'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
|
||||
{'path': '/properties/memory_mb', 'value': '12288', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '999', 'op': 'add'}
|
||||
]
|
||||
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patch, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch')
|
||||
def test_compat_ok(self, mock_patch):
|
||||
patch = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
|
||||
]
|
||||
|
||||
self.hook.before_update(self.old_data, self.node_info)
|
||||
self.assertCalledWithPatch(patch, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch')
|
||||
def test_no_overwrite(self, mock_patch):
|
||||
CONF.set_override('overwrite_existing', False, 'processing')
|
||||
@ -98,36 +63,21 @@ class TestSchedulerHook(test_base.NodeTest):
|
||||
'cpu_arch': 'i686'
|
||||
}
|
||||
patch = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
|
||||
{'path': '/properties/cpus', 'value': '4', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '999', 'op': 'add'}
|
||||
]
|
||||
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
self.assertCalledWithPatch(patch, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch')
|
||||
def test_compat_root_disk(self, mock_patch):
|
||||
self.old_data['root_disk'] = {'name': '/dev/sda',
|
||||
'size': 42 * units.Gi}
|
||||
patch = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '41', 'op': 'add'}
|
||||
]
|
||||
|
||||
self.hook.before_update(self.old_data, self.node_info)
|
||||
self.assertCalledWithPatch(patch, mock_patch)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'patch')
|
||||
def test_root_disk_no_spacing(self, mock_patch):
|
||||
CONF.set_override('disk_partitioning_spacing', False, 'processing')
|
||||
self.data['root_disk'] = {'name': '/dev/sda', 'size': 42 * units.Gi}
|
||||
patch = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/cpus', 'value': '4', 'op': 'add'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '42', 'op': 'add'}
|
||||
{'path': '/properties/memory_mb', 'value': '12288', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '1000', 'op': 'add'}
|
||||
]
|
||||
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
@ -138,38 +88,9 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
def setUp(self):
|
||||
super(TestValidateInterfacesHook, self).setUp()
|
||||
self.hook = std_plugins.ValidateInterfacesHook()
|
||||
self.data = {
|
||||
'inventory': {
|
||||
'interfaces': [
|
||||
{'name': 'em1', 'mac_address': '11:11:11:11:11:11',
|
||||
'ipv4_address': '1.1.1.1'},
|
||||
{'name': 'em2', 'mac_address': '22:22:22:22:22:22',
|
||||
'ipv4_address': '2.2.2.2'},
|
||||
{'name': 'em3', 'mac_address': '33:33:33:33:33:33',
|
||||
'ipv4_address': None},
|
||||
],
|
||||
},
|
||||
'boot_interface': '01-22-22-22-22-22-22'
|
||||
}
|
||||
self.old_data = {
|
||||
'interfaces': {
|
||||
'em1': {'mac': '11:11:11:11:11:11', 'ip': '1.1.1.1'},
|
||||
'em2': {'mac': '22:22:22:22:22:22', 'ip': '2.2.2.2'},
|
||||
'em3': {'mac': '33:33:33:33:33:33'}
|
||||
},
|
||||
'boot_interface': '01-22-22-22-22-22-22',
|
||||
}
|
||||
self.orig_interfaces = self.old_data['interfaces'].copy()
|
||||
self.orig_interfaces['em3']['ip'] = None
|
||||
self.pxe_interface = self.old_data['interfaces']['em2']
|
||||
self.active_interfaces = {
|
||||
'em1': {'mac': '11:11:11:11:11:11', 'ip': '1.1.1.1'},
|
||||
'em2': {'mac': '22:22:22:22:22:22', 'ip': '2.2.2.2'},
|
||||
}
|
||||
|
||||
self.existing_ports = [mock.Mock(spec=['address', 'uuid'],
|
||||
address=a)
|
||||
for a in ('11:11:11:11:11:11',
|
||||
for a in (self.macs[1],
|
||||
'44:44:44:44:44:44')]
|
||||
self.node_info = node_cache.NodeInfo(uuid=self.uuid, started_at=0,
|
||||
node=self.node,
|
||||
@ -190,29 +111,31 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
self.assertRaises(SystemExit, std_plugins.ValidateInterfacesHook)
|
||||
|
||||
def test_no_interfaces(self):
|
||||
self.assertRaisesRegexp(utils.Error, 'No interfaces',
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'Hardware inventory is empty or missing',
|
||||
self.hook.before_processing, {})
|
||||
self.assertRaisesRegexp(utils.Error, 'No interfaces',
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'Hardware inventory is empty or missing',
|
||||
self.hook.before_processing, {'inventory': {}})
|
||||
self.assertRaisesRegexp(utils.Error, 'No interfaces',
|
||||
self.hook.before_processing, {'inventory': {
|
||||
'interfaces': []
|
||||
}})
|
||||
del self.inventory['interfaces']
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'interfaces key is missing or empty',
|
||||
self.hook.before_processing, self.data)
|
||||
|
||||
def test_only_pxe(self):
|
||||
self.hook.before_processing(self.data)
|
||||
|
||||
self.assertEqual({'em2': self.pxe_interface}, self.data['interfaces'])
|
||||
self.assertEqual([self.pxe_interface['mac']], self.data['macs'])
|
||||
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
|
||||
self.assertEqual(self.pxe_interfaces, self.data['interfaces'])
|
||||
self.assertEqual([self.pxe_mac], self.data['macs'])
|
||||
self.assertEqual(self.all_interfaces, self.data['all_interfaces'])
|
||||
|
||||
def test_only_pxe_mac_format(self):
|
||||
self.data['boot_interface'] = '22:22:22:22:22:22'
|
||||
self.data['boot_interface'] = self.pxe_mac
|
||||
self.hook.before_processing(self.data)
|
||||
|
||||
self.assertEqual({'em2': self.pxe_interface}, self.data['interfaces'])
|
||||
self.assertEqual([self.pxe_interface['mac']], self.data['macs'])
|
||||
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
|
||||
self.assertEqual(self.pxe_interfaces, self.data['interfaces'])
|
||||
self.assertEqual([self.pxe_mac], self.data['macs'])
|
||||
self.assertEqual(self.all_interfaces, self.data['all_interfaces'])
|
||||
|
||||
def test_only_pxe_not_found(self):
|
||||
self.data['boot_interface'] = 'aa:bb:cc:dd:ee:ff'
|
||||
@ -227,7 +150,7 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
self.assertEqual(sorted(i['mac'] for i in
|
||||
self.active_interfaces.values()),
|
||||
sorted(self.data['macs']))
|
||||
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
|
||||
self.assertEqual(self.all_interfaces, self.data['all_interfaces'])
|
||||
|
||||
def test_only_active(self):
|
||||
CONF.set_override('add_ports', 'active', 'processing')
|
||||
@ -237,52 +160,43 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
self.assertEqual(sorted(i['mac'] for i in
|
||||
self.active_interfaces.values()),
|
||||
sorted(self.data['macs']))
|
||||
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
|
||||
self.assertEqual(self.all_interfaces, self.data['all_interfaces'])
|
||||
|
||||
def test_all(self):
|
||||
CONF.set_override('add_ports', 'all', 'processing')
|
||||
self.hook.before_processing(self.data)
|
||||
|
||||
self.assertEqual(self.orig_interfaces, self.data['interfaces'])
|
||||
self.assertEqual(self.all_interfaces, self.data['interfaces'])
|
||||
self.assertEqual(sorted(i['mac'] for i in
|
||||
self.orig_interfaces.values()),
|
||||
self.all_interfaces.values()),
|
||||
sorted(self.data['macs']))
|
||||
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
|
||||
self.assertEqual(self.all_interfaces, self.data['all_interfaces'])
|
||||
|
||||
def test_malformed_interfaces(self):
|
||||
self.data = {
|
||||
'inventory': {
|
||||
'interfaces': [
|
||||
# no name
|
||||
{'mac_address': '11:11:11:11:11:11',
|
||||
'ipv4_address': '1.1.1.1'},
|
||||
# empty
|
||||
{},
|
||||
],
|
||||
},
|
||||
}
|
||||
self.inventory['interfaces'] = [
|
||||
# no name
|
||||
{'mac_address': '11:11:11:11:11:11', 'ipv4_address': '1.1.1.1'},
|
||||
# empty
|
||||
{},
|
||||
]
|
||||
self.assertRaisesRegexp(utils.Error, 'No interfaces supplied',
|
||||
self.hook.before_processing, self.data)
|
||||
|
||||
def test_skipped_interfaces(self):
|
||||
CONF.set_override('add_ports', 'all', 'processing')
|
||||
self.data = {
|
||||
'inventory': {
|
||||
'interfaces': [
|
||||
# local interface (by name)
|
||||
{'name': 'lo', 'mac_address': '11:11:11:11:11:11',
|
||||
'ipv4_address': '1.1.1.1'},
|
||||
# local interface (by IP address)
|
||||
{'name': 'em1', 'mac_address': '22:22:22:22:22:22',
|
||||
'ipv4_address': '127.0.0.1'},
|
||||
# no MAC provided
|
||||
{'name': 'em3', 'ipv4_address': '2.2.2.2'},
|
||||
# malformed MAC provided
|
||||
{'name': 'em4', 'mac_address': 'foobar',
|
||||
'ipv4_address': '2.2.2.2'},
|
||||
],
|
||||
},
|
||||
}
|
||||
self.inventory['interfaces'] = [
|
||||
# local interface (by name)
|
||||
{'name': 'lo', 'mac_address': '11:11:11:11:11:11',
|
||||
'ipv4_address': '1.1.1.1'},
|
||||
# local interface (by IP address)
|
||||
{'name': 'em1', 'mac_address': '22:22:22:22:22:22',
|
||||
'ipv4_address': '127.0.0.1'},
|
||||
# no MAC provided
|
||||
{'name': 'em3', 'ipv4_address': '2.2.2.2'},
|
||||
# malformed MAC provided
|
||||
{'name': 'em4', 'mac_address': 'foobar',
|
||||
'ipv4_address': '2.2.2.2'},
|
||||
]
|
||||
self.assertRaisesRegexp(utils.Error, 'No suitable interfaces found',
|
||||
self.hook.before_processing, self.data)
|
||||
|
||||
@ -294,7 +208,7 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
@mock.patch.object(node_cache.NodeInfo, 'delete_port')
|
||||
def test_keep_present(self, mock_delete_port):
|
||||
CONF.set_override('keep_ports', 'present', 'processing')
|
||||
self.data['all_interfaces'] = self.orig_interfaces
|
||||
self.data['all_interfaces'] = self.all_interfaces
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
|
||||
mock_delete_port.assert_called_once_with(self.existing_ports[1])
|
||||
@ -302,7 +216,7 @@ class TestValidateInterfacesHook(test_base.NodeTest):
|
||||
@mock.patch.object(node_cache.NodeInfo, 'delete_port')
|
||||
def test_keep_added(self, mock_delete_port):
|
||||
CONF.set_override('keep_ports', 'added', 'processing')
|
||||
self.data['macs'] = [self.pxe_interface['mac']]
|
||||
self.data['macs'] = [self.pxe_mac]
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
|
||||
mock_delete_port.assert_any_call(self.existing_ports[0])
|
||||
@ -313,28 +227,21 @@ class TestRootDiskSelection(test_base.NodeTest):
|
||||
def setUp(self):
|
||||
super(TestRootDiskSelection, self).setUp()
|
||||
self.hook = std_plugins.RootDiskSelectionHook()
|
||||
self.data = {
|
||||
'inventory': {
|
||||
'disks': [
|
||||
{'model': 'Model 1', 'size': 20 * units.Gi,
|
||||
'name': '/dev/sdb'},
|
||||
{'model': 'Model 2', 'size': 5 * units.Gi,
|
||||
'name': '/dev/sda'},
|
||||
{'model': 'Model 3', 'size': 10 * units.Gi,
|
||||
'name': '/dev/sdc'},
|
||||
{'model': 'Model 4', 'size': 4 * units.Gi,
|
||||
'name': '/dev/sdd'},
|
||||
{'model': 'Too Small', 'size': 1 * units.Gi,
|
||||
'name': '/dev/sde'},
|
||||
]
|
||||
}
|
||||
}
|
||||
self.matched = self.data['inventory']['disks'][2].copy()
|
||||
self.inventory['disks'] = [
|
||||
{'model': 'Model 1', 'size': 20 * units.Gi, 'name': '/dev/sdb'},
|
||||
{'model': 'Model 2', 'size': 5 * units.Gi, 'name': '/dev/sda'},
|
||||
{'model': 'Model 3', 'size': 10 * units.Gi, 'name': '/dev/sdc'},
|
||||
{'model': 'Model 4', 'size': 4 * units.Gi, 'name': '/dev/sdd'},
|
||||
{'model': 'Too Small', 'size': 1 * units.Gi, 'name': '/dev/sde'},
|
||||
]
|
||||
self.matched = self.inventory['disks'][2].copy()
|
||||
self.node_info = mock.Mock(spec=node_cache.NodeInfo,
|
||||
uuid=self.uuid,
|
||||
**{'node.return_value': self.node})
|
||||
|
||||
def test_no_hints(self):
|
||||
del self.data['root_disk']
|
||||
|
||||
self.hook.before_update(self.data, self.node_info)
|
||||
|
||||
self.assertNotIn('local_gb', self.data)
|
||||
@ -343,9 +250,10 @@ class TestRootDiskSelection(test_base.NodeTest):
|
||||
def test_no_inventory(self):
|
||||
self.node.properties['root_device'] = {'model': 'foo'}
|
||||
del self.data['inventory']
|
||||
del self.data['root_disk']
|
||||
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'requires ironic-python-agent',
|
||||
'Hardware inventory is empty or missing',
|
||||
self.hook.before_update,
|
||||
self.data, self.node_info)
|
||||
|
||||
@ -354,10 +262,10 @@ class TestRootDiskSelection(test_base.NodeTest):
|
||||
|
||||
def test_no_disks(self):
|
||||
self.node.properties['root_device'] = {'size': 10}
|
||||
self.data['inventory']['disks'] = []
|
||||
self.inventory['disks'] = []
|
||||
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'No disks found',
|
||||
'disks key is missing or empty',
|
||||
self.hook.before_update,
|
||||
self.data, self.node_info)
|
||||
|
||||
@ -379,6 +287,7 @@ class TestRootDiskSelection(test_base.NodeTest):
|
||||
def test_one_fails(self):
|
||||
self.node.properties['root_device'] = {'size': 10,
|
||||
'model': 'Model 42'}
|
||||
del self.data['root_disk']
|
||||
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
'No disks satisfied root device hints',
|
||||
@ -402,15 +311,12 @@ class TestRootDiskSelection(test_base.NodeTest):
|
||||
self.data, self.node_info)
|
||||
|
||||
|
||||
class TestRamdiskError(test_base.BaseTest):
|
||||
class TestRamdiskError(test_base.InventoryTest):
|
||||
def setUp(self):
|
||||
super(TestRamdiskError, self).setUp()
|
||||
self.msg = 'BOOM'
|
||||
self.bmc_address = '1.2.3.4'
|
||||
self.data = {
|
||||
'error': self.msg,
|
||||
'ipmi_address': self.bmc_address,
|
||||
}
|
||||
self.data['error'] = self.msg
|
||||
|
||||
def test_no_logs(self):
|
||||
self.assertRaisesRegexp(utils.Error,
|
||||
|
@ -43,24 +43,9 @@ class BaseTest(test_base.NodeTest):
|
||||
def setUp(self):
|
||||
super(BaseTest, self).setUp()
|
||||
self.started_at = time.time()
|
||||
self.pxe_mac = self.macs[1]
|
||||
self.data = {
|
||||
'ipmi_address': self.bmc_address,
|
||||
'cpus': 2,
|
||||
'cpu_arch': 'x86_64',
|
||||
'memory_mb': 1024,
|
||||
'local_gb': 20,
|
||||
'interfaces': {
|
||||
'em1': {'mac': self.macs[0], 'ip': '1.2.0.1'},
|
||||
'em2': {'mac': self.macs[1], 'ip': '1.2.0.2'},
|
||||
'em3': {'mac': 'DE:AD:BE:EF:DE:AD'},
|
||||
},
|
||||
'boot_interface': '01-' + self.pxe_mac.replace(':', '-'),
|
||||
}
|
||||
self.all_ports = [mock.Mock(uuid=uuidutils.generate_uuid(),
|
||||
address=mac) for mac in self.macs]
|
||||
self.ports = [self.all_ports[1]]
|
||||
self.all_macs = self.macs + ['DE:AD:BE:EF:DE:AD']
|
||||
self.fake_result_json = 'node json'
|
||||
|
||||
self.cli_fixture = self.useFixture(
|
||||
@ -94,10 +79,6 @@ class TestProcess(BaseProcessTest):
|
||||
|
||||
self.assertEqual(self.fake_result_json, res)
|
||||
|
||||
# Only boot interface is added by default
|
||||
self.assertEqual(['em2'], sorted(self.data['interfaces']))
|
||||
self.assertEqual([self.pxe_mac], self.data['macs'])
|
||||
|
||||
self.find_mock.assert_called_once_with(bmc_address=self.bmc_address,
|
||||
mac=mock.ANY)
|
||||
actual_macs = self.find_mock.call_args[1]['mac']
|
||||
@ -107,7 +88,7 @@ class TestProcess(BaseProcessTest):
|
||||
self.node, self.data, self.node_info)
|
||||
|
||||
def test_no_ipmi(self):
|
||||
del self.data['ipmi_address']
|
||||
del self.inventory['bmc_address']
|
||||
process.process(self.data)
|
||||
|
||||
self.find_mock.assert_called_once_with(bmc_address=None, mac=mock.ANY)
|
||||
@ -350,15 +331,8 @@ class TestProcessNode(BaseTest):
|
||||
'processing')
|
||||
self.validate_attempts = 5
|
||||
self.data['macs'] = self.macs # validate_interfaces hook
|
||||
self.data['all_interfaces'] = self.data['interfaces']
|
||||
self.ports = self.all_ports
|
||||
|
||||
self.patch_props = [
|
||||
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
|
||||
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
|
||||
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
|
||||
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
|
||||
] # scheduler hook
|
||||
self.new_creds = ('user', 'password')
|
||||
self.patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
@ -406,24 +380,12 @@ class TestProcessNode(BaseTest):
|
||||
address=self.macs[0])
|
||||
self.cli.port.create.assert_any_call(node_uuid=self.uuid,
|
||||
address=self.macs[1])
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.assertFalse(self.cli.node.validate.called)
|
||||
|
||||
post_hook_mock.assert_called_once_with(self.data, self.node_info)
|
||||
finished_mock.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_overwrite_disabled(self):
|
||||
CONF.set_override('overwrite_existing', False, 'processing')
|
||||
patch = [
|
||||
{'op': 'add', 'path': '/properties/cpus', 'value': '2'},
|
||||
{'op': 'add', 'path': '/properties/memory_mb', 'value': '1024'},
|
||||
]
|
||||
|
||||
process._process_node(self.node, self.data, self.node_info)
|
||||
|
||||
self.assertCalledWithPatch(patch, self.cli.node.update)
|
||||
|
||||
def test_port_failed(self):
|
||||
self.cli.port.create.side_effect = (
|
||||
[exceptions.Conflict()] + self.ports[1:])
|
||||
@ -434,7 +396,6 @@ class TestProcessNode(BaseTest):
|
||||
address=self.macs[0])
|
||||
self.cli.port.create.assert_any_call(node_uuid=self.uuid,
|
||||
address=self.macs[1])
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
|
||||
def test_set_ipmi_credentials(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
@ -486,7 +447,6 @@ class TestProcessNode(BaseTest):
|
||||
process._process_node(self.node, self.data, self.node_info)
|
||||
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
finished_mock.assert_called_once_with(
|
||||
mock.ANY,
|
||||
error='Failed to power off node %s, check its power '
|
||||
@ -517,22 +477,19 @@ class TestProcessNode(BaseTest):
|
||||
swift_conn.create_object.assert_called_once_with(name, mock.ANY)
|
||||
self.assertEqual(expected,
|
||||
json.loads(swift_conn.create_object.call_args[0][1]))
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
|
||||
@mock.patch.object(process.swift, 'SwiftAPI', autospec=True)
|
||||
def test_store_data_no_logs(self, swift_mock):
|
||||
CONF.set_override('store_data', 'swift', 'processing')
|
||||
swift_conn = swift_mock.return_value
|
||||
name = 'inspector_data-%s' % self.uuid
|
||||
expected = self.data.copy()
|
||||
self.data['logs'] = 'something'
|
||||
|
||||
process._process_node(self.node, self.data, self.node_info)
|
||||
|
||||
swift_conn.create_object.assert_called_once_with(name, mock.ANY)
|
||||
self.assertEqual(expected,
|
||||
self.assertNotIn('logs',
|
||||
json.loads(swift_conn.create_object.call_args[0][1]))
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
|
||||
@mock.patch.object(process.swift, 'SwiftAPI', autospec=True)
|
||||
def test_store_data_location(self, swift_mock):
|
||||
@ -541,11 +498,8 @@ class TestProcessNode(BaseTest):
|
||||
'processing')
|
||||
swift_conn = swift_mock.return_value
|
||||
name = 'inspector_data-%s' % self.uuid
|
||||
self.patch_props.append(
|
||||
{'path': '/extra/inspector_data_object',
|
||||
'value': name,
|
||||
'op': 'add'}
|
||||
)
|
||||
patch = [{'path': '/extra/inspector_data_object',
|
||||
'value': name, 'op': 'add'}]
|
||||
expected = self.data
|
||||
|
||||
process._process_node(self.node, self.data, self.node_info)
|
||||
@ -553,7 +507,7 @@ class TestProcessNode(BaseTest):
|
||||
swift_conn.create_object.assert_called_once_with(name, mock.ANY)
|
||||
self.assertEqual(expected,
|
||||
json.loads(swift_conn.create_object.call_args[0][1]))
|
||||
self.assertCalledWithPatch(self.patch_props, self.cli.node.update)
|
||||
self.cli.node.update.assert_any_call(self.uuid, patch)
|
||||
|
||||
|
||||
@mock.patch.object(process, '_reapply', autospec=True)
|
||||
@ -614,7 +568,6 @@ class TestReapplyNode(BaseTest):
|
||||
'processing')
|
||||
CONF.set_override('store_data', 'swift', 'processing')
|
||||
self.data['macs'] = self.macs
|
||||
self.data['all_interfaces'] = self.data['interfaces']
|
||||
self.ports = self.all_ports
|
||||
self.node_info = node_cache.NodeInfo(uuid=self.uuid,
|
||||
started_at=self.started_at,
|
||||
@ -667,8 +620,7 @@ class TestReapplyNode(BaseTest):
|
||||
finished_mock.assert_called_once_with(self.node_info)
|
||||
|
||||
# asserting validate_interfaces was called
|
||||
self.assertEqual({'em2': self.data['interfaces']['em2']},
|
||||
swifted_data['interfaces'])
|
||||
self.assertEqual(self.pxe_interfaces, swifted_data['interfaces'])
|
||||
self.assertEqual([self.pxe_mac], swifted_data['macs'])
|
||||
|
||||
# assert ports were created with whatever there was left
|
||||
|
@ -205,3 +205,22 @@ def get_valid_macs(data):
|
||||
return [m['mac']
|
||||
for m in data.get('all_interfaces', {}).values()
|
||||
if m.get('mac')]
|
||||
|
||||
|
||||
_INVENTORY_MANDATORY_KEYS = ('disks', 'memory', 'cpu', 'interfaces')
|
||||
|
||||
|
||||
def get_inventory(data, node_info=None):
|
||||
"""Get and validate the hardware inventory from introspection data."""
|
||||
inventory = data.get('inventory')
|
||||
# TODO(dtantsur): validate inventory using JSON schema
|
||||
if not inventory:
|
||||
raise Error(_('Hardware inventory is empty or missing'),
|
||||
data=data, node_info=node_info)
|
||||
|
||||
for key in _INVENTORY_MANDATORY_KEYS:
|
||||
if not inventory.get(key):
|
||||
raise Error(_('Invalid hardware inventory: %s key is missing '
|
||||
'or empty') % key, data=data, node_info=node_info)
|
||||
|
||||
return inventory
|
||||
|
7
releasenotes/notes/no-old-ramdisk-095b05e1245131d8.yaml
Normal file
7
releasenotes/notes/no-old-ramdisk-095b05e1245131d8.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
prelude: >
|
||||
Starting with this release only ironic-python-agent (IPA) is supported
|
||||
as an introspection ramdisk.
|
||||
upgrade:
|
||||
- Support for the old bash-based ramdisk was removed. Please switch to IPA
|
||||
before upgrading.
|
Loading…
Reference in New Issue
Block a user