summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/admin/drivers/redfish.rst2
-rw-r--r--ironic/common/exception.py4
-rw-r--r--ironic/common/release_mappings.py1
-rw-r--r--ironic/conductor/manager.py3
-rw-r--r--ironic/dhcp/neutron.py9
-rw-r--r--ironic/drivers/modules/console_utils.py23
-rw-r--r--ironic/drivers/modules/ipmitool.py7
-rw-r--r--ironic/objects/__init__.py1
-rw-r--r--ironic/objects/deployment.py259
-rw-r--r--ironic/tests/unit/common/test_release_mappings.py2
-rw-r--r--ironic/tests/unit/conductor/test_manager.py22
-rw-r--r--ironic/tests/unit/dhcp/test_neutron.py26
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_bios.py12
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_boot.py5
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_periodic_task.py73
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_power.py21
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_raid.py121
-rw-r--r--ironic/tests/unit/drivers/modules/test_console_utils.py43
-rw-r--r--ironic/tests/unit/drivers/modules/test_ipmitool.py5
-rw-r--r--ironic/tests/unit/objects/test_deployment.py117
-rw-r--r--ironic/tests/unit/objects/test_objects.py1
-rw-r--r--releasenotes/notes/add-ansible-python-interpreter-2035e0f23d407aaf.yaml4
-rw-r--r--releasenotes/notes/fixes-get-boot-option-for-software-raid-baa2cffd95e1f624.yaml2
-rw-r--r--releasenotes/notes/skip-power-sync-for-adoptfail-d2498f1a2e997ed7.yaml5
-rw-r--r--releasenotes/notes/socat-console-port-alloc-ipv6-26760f53f86209d0.yaml5
-rw-r--r--tox.ini1
26 files changed, 666 insertions, 108 deletions
diff --git a/doc/source/admin/drivers/redfish.rst b/doc/source/admin/drivers/redfish.rst
index f784740dc..01352302a 100644
--- a/doc/source/admin/drivers/redfish.rst
+++ b/doc/source/admin/drivers/redfish.rst
@@ -188,7 +188,7 @@ the same purpose.
Virtual Media Ramdisk
~~~~~~~~~~~~~~~~~~~~~
-The ``ramdisk`` deploy interface can be used in concert with the the
+The ``ramdisk`` deploy interface can be used in concert with the
``redfish-virtual-media`` boot interface to facilitate the boot of a remote
node utilizing pre-supplied virtual media.
diff --git a/ironic/common/exception.py b/ironic/common/exception.py
index 1ade17253..912121a96 100644
--- a/ironic/common/exception.py
+++ b/ironic/common/exception.py
@@ -297,6 +297,10 @@ class InvalidIPv4Address(IronicException):
_msg_fmt = _("Invalid IPv4 address %(ip_address)s.")
+class InvalidIPAddress(IronicException):
+ _msg_fmt = _("Invalid IP address %(ip_address)s.")
+
+
class FailedToUpdateMacOnPort(IronicException):
_msg_fmt = _("Update MAC address on port: %(port_id)s failed.")
diff --git a/ironic/common/release_mappings.py b/ironic/common/release_mappings.py
index 77d7f2500..bc9565a98 100644
--- a/ironic/common/release_mappings.py
+++ b/ironic/common/release_mappings.py
@@ -255,6 +255,7 @@ RELEASE_MAPPING = {
'Node': ['1.35'],
'Conductor': ['1.3'],
'Chassis': ['1.3'],
+ 'Deployment': ['1.0'],
'DeployTemplate': ['1.1'],
'Port': ['1.9'],
'Portgroup': ['1.4'],
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index cb20fcfd8..356403bab 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -81,7 +81,8 @@ LOG = log.getLogger(__name__)
METRICS = metrics_utils.get_metrics_logger(__name__)
-SYNC_EXCLUDED_STATES = (states.DEPLOYWAIT, states.CLEANWAIT, states.ENROLL)
+SYNC_EXCLUDED_STATES = (states.DEPLOYWAIT, states.CLEANWAIT, states.ENROLL,
+ states.ADOPTFAIL)
class ConductorManager(base_manager.BaseConductorManager):
diff --git a/ironic/dhcp/neutron.py b/ironic/dhcp/neutron.py
index 372858742..bf42266c5 100644
--- a/ironic/dhcp/neutron.py
+++ b/ironic/dhcp/neutron.py
@@ -187,18 +187,19 @@ class NeutronDHCPApi(base.BaseDHCP):
if ip_address:
try:
- if ipaddress.ip_address(ip_address).version == 4:
+ if (ipaddress.ip_address(ip_address).version == 4
+ or ipaddress.ip_address(ip_address).version == 6):
return ip_address
else:
- LOG.error("Neutron returned invalid IPv4 "
+ LOG.error("Neutron returned invalid IP "
"address %(ip_address)s on port %(port_uuid)s.",
{'ip_address': ip_address,
'port_uuid': port_uuid})
- raise exception.InvalidIPv4Address(ip_address=ip_address)
+ raise exception.InvalidIPAddress(ip_address=ip_address)
except ValueError as exc:
LOG.error("An Invalid IP address was supplied and failed "
"basic validation: %s", exc)
- raise exception.InvalidIPv4Address(ip_address=ip_address)
+ raise exception.InvalidIPAddress(ip_address=ip_address)
else:
LOG.error("No IP address assigned to Neutron port %s.",
port_uuid)
diff --git a/ironic/drivers/modules/console_utils.py b/ironic/drivers/modules/console_utils.py
index b2f92ba3d..6e08b6712 100644
--- a/ironic/drivers/modules/console_utils.py
+++ b/ironic/drivers/modules/console_utils.py
@@ -162,11 +162,24 @@ def _get_port_range():
return start, stop
-def _verify_port(port):
+def _verify_port(port, host=None):
"""Check whether specified port is in use."""
- s = socket.socket()
+ ip_version = None
+ if host is not None:
+ try:
+ ip_version = ipaddress.ip_address(host).version
+ except ValueError:
+ # Assume it's a hostname
+ pass
+ else:
+ host = CONF.host
+ if ip_version == 6:
+ s = socket.socket(socket.AF_INET6)
+ else:
+ s = socket.socket()
+
try:
- s.bind((CONF.host, port))
+ s.bind((host, port))
except socket.error:
raise exception.Conflict()
finally:
@@ -174,7 +187,7 @@ def _verify_port(port):
@lockutils.synchronized(SERIAL_LOCK)
-def acquire_port():
+def acquire_port(host=None):
"""Returns a free TCP port on current host.
Find and returns a free TCP port in the range
@@ -187,7 +200,7 @@ def acquire_port():
if port in ALLOCATED_PORTS:
continue
try:
- _verify_port(port)
+ _verify_port(port, host=host)
ALLOCATED_PORTS.add(port)
return port
except exception.Conflict:
diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py
index 85beca183..a3b443b58 100644
--- a/ironic/drivers/modules/ipmitool.py
+++ b/ironic/drivers/modules/ipmitool.py
@@ -807,10 +807,10 @@ def _constructor_checks(driver):
_check_temp_dir()
-def _allocate_port(task):
+def _allocate_port(task, host=None):
node = task.node
dii = node.driver_internal_info or {}
- allocated_port = console_utils.acquire_port()
+ allocated_port = console_utils.acquire_port(host=host)
dii['allocated_ipmi_terminal_port'] = allocated_port
node.driver_internal_info = dii
node.save()
@@ -1411,7 +1411,8 @@ class IPMISocatConsole(IPMIConsole):
"""
driver_info = _parse_driver_info(task.node)
if not driver_info['port']:
- driver_info['port'] = _allocate_port(task)
+ driver_info['port'] = _allocate_port(
+ task, host=CONF.console.socat_address)
try:
self._exec_stop_console(driver_info)
diff --git a/ironic/objects/__init__.py b/ironic/objects/__init__.py
index 2afe75003..7f199c6aa 100644
--- a/ironic/objects/__init__.py
+++ b/ironic/objects/__init__.py
@@ -29,6 +29,7 @@ def register_all():
__import__('ironic.objects.chassis')
__import__('ironic.objects.conductor')
__import__('ironic.objects.deploy_template')
+ __import__('ironic.objects.deployment')
__import__('ironic.objects.node')
__import__('ironic.objects.port')
__import__('ironic.objects.portgroup')
diff --git a/ironic/objects/deployment.py b/ironic/objects/deployment.py
new file mode 100644
index 000000000..7fe7f7544
--- /dev/null
+++ b/ironic/objects/deployment.py
@@ -0,0 +1,259 @@
+# 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.
+
+from oslo_utils import uuidutils
+from oslo_versionedobjects import base as object_base
+
+from ironic.common import exception
+from ironic.db import api as dbapi
+from ironic.objects import base
+from ironic.objects import fields as object_fields
+from ironic.objects import node as node_obj
+
+
+@base.IronicObjectRegistry.register
+class Deployment(base.IronicObject, object_base.VersionedObjectDictCompat):
+ # Version 1.0: Initial version
+ VERSION = '1.0'
+
+ dbapi = dbapi.get_instance()
+
+ fields = {
+ 'uuid': object_fields.UUIDField(nullable=True),
+ 'node_uuid': object_fields.UUIDField(nullable=True),
+ 'image_checksum': object_fields.StringField(nullable=True),
+ 'image_ref': object_fields.StringField(nullable=True),
+ 'kernel_ref': object_fields.StringField(nullable=True),
+ 'ramdisk_ref': object_fields.StringField(nullable=True),
+ 'root_device': object_fields.FlexibleDictField(nullable=True),
+ 'root_gib': object_fields.IntegerField(nullable=True),
+ 'state': object_fields.StringField(nullable=True),
+ 'swap_mib': object_fields.IntegerField(nullable=True),
+ }
+
+ node_mapping = {
+ 'instance_uuid': 'uuid',
+ 'provision_state': 'state',
+ 'uuid': 'node_uuid',
+ }
+
+ instance_info_mapping = {
+ 'image_checksum': 'image_checksum',
+ 'image_source': 'image_ref',
+ 'kernel': 'kernel_ref',
+ 'ramdisk': 'ramdisk_ref',
+ 'root_device': 'root_device',
+ 'root_gb': 'root_gib',
+ 'swap_mb': 'swap_mib',
+ }
+
+ instance_info_mapping_rev = {v: k
+ for k, v in instance_info_mapping.items()}
+
+ assert (set(node_mapping.values()) | set(instance_info_mapping.values())
+ == set(fields))
+
+ def _convert_to_version(self, target_version,
+ remove_unavailable_fields=True):
+ """Convert to the target version.
+
+ Convert the object to the target version. The target version may be
+ the same, older, or newer than the version of the object. This is
+ used for DB interactions as well as for serialization/deserialization.
+
+ :param target_version: the desired version of the object
+ :param remove_unavailable_fields: True to remove fields that are
+ unavailable in the target version; set this to True when
+ (de)serializing. False to set the unavailable fields to appropriate
+ values; set this to False for DB interactions.
+ """
+
+ @classmethod
+ def _from_node_object(cls, context, node):
+ """Convert a node into a virtual `Deployment` object."""
+ result = cls(context)
+ result._update_from_node_object(node)
+ return result
+
+ def _update_from_node_object(self, node):
+ """Update the Deployment object from the node."""
+ for src, dest in self.node_mapping.items():
+ setattr(self, dest, getattr(node, src, None))
+ for src, dest in self.instance_info_mapping.items():
+ setattr(self, dest, node.instance_info.get(src))
+
+ def _update_node_object(self, node):
+ """Update the given node object with the changes here."""
+ changes = self.obj_get_changes()
+ try:
+ new_instance_uuid = changes.pop('uuid')
+ except KeyError:
+ pass
+ else:
+ node.instance_uuid = new_instance_uuid
+
+ changes.pop('node_uuid', None)
+ instance_info = node.instance_info
+
+ for field, value in changes.items():
+ # NOTE(dtantsur): only instance_info fields can be updated here.
+ try:
+ dest = self.instance_info_mapping_rev[field]
+ except KeyError:
+ # NOTE(dtantsur): this should not happen because of API-level
+ # validations, but checking just in case.
+ raise exception.BadRequest('Field %s cannot be set or updated'
+ % changes)
+ instance_info[dest] = value
+
+ node.instance_info = instance_info
+ return node
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable_classmethod
+ @classmethod
+ def get_by_uuid(cls, context, uuid):
+ """Find a deployment by its UUID.
+
+ :param cls: the :class:`Deployment`
+ :param context: Security context
+ :param uuid: The UUID of a deployment.
+ :returns: An :class:`Deployment` object.
+ :raises: InstanceNotFound
+
+ """
+ node = node_obj.Node.get_by_instance_uuid(context, uuid)
+ return cls._from_node_object(context, node)
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable_classmethod
+ @classmethod
+ def get_by_node_uuid(cls, context, node_uuid):
+ """Find a deployment based by its node's UUID.
+
+ :param cls: the :class:`Deployment`
+ :param context: Security context
+ :param node_uuid: The UUID of a corresponding node.
+ :returns: An :class:`Deployment` object.
+ :raises: NodeNotFound
+
+ """
+ node = node_obj.Node.get_by_uuid(context, node_uuid)
+ return cls._from_node_object(context, node)
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable_classmethod
+ @classmethod
+ def list(cls, context, filters=None, limit=None, marker=None,
+ sort_key=None, sort_dir=None):
+ """Return a list of Deployment objects.
+
+ :param cls: the :class:`Deployment`
+ :param context: Security context.
+ :param filters: Filters to apply.
+ :param limit: Maximum number of resources to return in a single result.
+ :param marker: Pagination marker for large data sets.
+ :param sort_key: Column to sort results by.
+ :param sort_dir: Direction to sort. "asc" or "desc".
+ :returns: A list of :class:`Deployment` object.
+ :raises: InvalidParameterValue
+
+ """
+ nodes = node_obj.Node.list(context, filters=filters, limit=limit,
+ marker=marker, sort_key=sort_key,
+ sort_dir=sort_dir)
+ return [cls._from_node_object(context, node) for node in nodes]
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable
+ def create(self, context=None, node=None):
+ """Create a Deployment.
+
+ Updates the corresponding node under the hood.
+
+ :param context: Security context. NOTE: This should only
+ be used internally by the indirection_api.
+ Unfortunately, RPC requires context as the first
+ argument, even though we don't use it.
+ A context should be set when instantiating the
+ object, e.g.: Deployment(context)
+ :param node: Node object for deployment.
+ :raises: InstanceAssociated, NodeAssociated, NodeNotFound
+
+ """
+ if node is None:
+ node = node_obj.Node.get_by_uuid(self._context, self.node_uuid)
+ elif 'node_uuid' in self and self.node_uuid:
+ # NOTE(dtantsur): this is only possible if a bug happens on
+ # a higher level.
+ assert self.node_uuid == node.uuid
+
+ if 'uuid' not in self or not self.uuid:
+ self.uuid = uuidutils.generate_uuid()
+ node.instance_uuid = self.uuid
+ self._update_node_object(node)
+ node.save()
+ self._update_from_node_object(node)
+ self.obj_reset_changes()
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable
+ def destroy(self, context=None, node=None):
+ """Delete the Deployment.
+
+ Updates the corresponding node under the hood.
+
+ :param context: Security context. NOTE: This should only
+ be used internally by the indirection_api.
+ Unfortunately, RPC requires context as the first
+ argument, even though we don't use it.
+ A context should be set when instantiating the
+ object, e.g.: Node(context)
+ :param node: Node object for deployment.
+ """
+ if node is None:
+ node = node_obj.Node.get_by_uuid(self._context, self.node_uuid)
+ else:
+ assert node.uuid == self.node_uuid
+ node.instance_uuid = None
+ node.instance_info = {}
+ node.save()
+ self._update_from_node_object(node)
+ self.obj_reset_changes()
+
+ # NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
+ # methods can be used in the future to replace current explicit RPC calls.
+ # Implications of calling new remote procedures should be thought through.
+ # @object_base.remotable
+ def refresh(self, context=None):
+ """Refresh the object by re-fetching from the DB.
+
+ :param context: Security context. NOTE: This should only
+ be used internally by the indirection_api.
+ Unfortunately, RPC requires context as the first
+ argument, even though we don't use it.
+ A context should be set when instantiating the
+ object, e.g.: Node(context)
+ """
+ current = self.get_by_uuid(self._context, self.uuid)
+ self.obj_refresh(current)
+ self.obj_reset_changes()
diff --git a/ironic/tests/unit/common/test_release_mappings.py b/ironic/tests/unit/common/test_release_mappings.py
index 2231d0be5..b5adfa060 100644
--- a/ironic/tests/unit/common/test_release_mappings.py
+++ b/ironic/tests/unit/common/test_release_mappings.py
@@ -92,6 +92,8 @@ class ReleaseMappingsTestCase(base.TestCase):
model_names -= exceptions
# NodeTrait maps to two objects
model_names |= set(['Trait', 'TraitList'])
+ # Deployment is purely virtual.
+ model_names.add('Deployment')
object_names = set(
release_mappings.RELEASE_MAPPING['master']['objects'])
self.assertEqual(model_names, object_names)
diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py
index 7ab03e175..52a5e03a3 100644
--- a/ironic/tests/unit/conductor/test_manager.py
+++ b/ironic/tests/unit/conductor/test_manager.py
@@ -5266,6 +5266,28 @@ class ManagerSyncPowerStatesTestCase(mgr_utils.CommonMixIn,
shared=True)
sync_mock.assert_called_once_with(task, mock.ANY)
+ def test_single_node_adopt_failed(self, get_nodeinfo_mock,
+ mapped_mock, acquire_mock, sync_mock):
+ get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()
+ mapped_mock.return_value = True
+ task = self._create_task(
+ node_attrs=dict(uuid=self.node.uuid,
+ provision_state=states.ADOPTFAIL))
+ acquire_mock.side_effect = self._get_acquire_side_effect(task)
+
+ self.service._sync_power_states(self.context)
+
+ get_nodeinfo_mock.assert_called_once_with(
+ columns=self.columns, filters=self.filters)
+ mapped_mock.assert_called_once_with(self.service,
+ self.node.uuid,
+ self.node.driver,
+ self.node.conductor_group)
+ acquire_mock.assert_called_once_with(self.context, self.node.uuid,
+ purpose=mock.ANY,
+ shared=True)
+ sync_mock.assert_not_called()
+
def test__sync_power_state_multiple_nodes(self, get_nodeinfo_mock,
mapped_mock, acquire_mock,
sync_mock):
diff --git a/ironic/tests/unit/dhcp/test_neutron.py b/ironic/tests/unit/dhcp/test_neutron.py
index e4091c58b..23a807da6 100644
--- a/ironic/tests/unit/dhcp/test_neutron.py
+++ b/ironic/tests/unit/dhcp/test_neutron.py
@@ -267,6 +267,30 @@ class TestNeutron(db_base.DbTestCase):
self.assertEqual(expected, result)
fake_client.show_port.assert_called_once_with(port_id)
+ def test__get_fixed_ip_address_ipv6(self):
+ port_id = 'fake-port-id'
+ expected = "2001:dead:beef::1234"
+ api = dhcp_factory.DHCPFactory().provider
+ port_data = {
+ "id": port_id,
+ "network_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6",
+ "admin_state_up": True,
+ "status": "ACTIVE",
+ "mac_address": "fa:16:3e:4c:2c:30",
+ "fixed_ips": [
+ {
+ "ip_address": "2001:dead:beef::1234",
+ "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+ }
+ ],
+ "device_id": 'bece68a3-2f8b-4e66-9092-244493d6aba7',
+ }
+ fake_client = mock.Mock()
+ fake_client.show_port.return_value = {'port': port_data}
+ result = api._get_fixed_ip_address(port_id, fake_client)
+ self.assertEqual(expected, result)
+ fake_client.show_port.assert_called_once_with(port_id)
+
def test__get_fixed_ip_address_invalid_ip(self):
port_id = 'fake-port-id'
api = dhcp_factory.DHCPFactory().provider
@@ -286,7 +310,7 @@ class TestNeutron(db_base.DbTestCase):
}
fake_client = mock.Mock()
fake_client.show_port.return_value = {'port': port_data}
- self.assertRaises(exception.InvalidIPv4Address,
+ self.assertRaises(exception.InvalidIPAddress,
api._get_fixed_ip_address,
port_id, fake_client)
fake_client.show_port.assert_called_once_with(port_id)
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_bios.py b/ironic/tests/unit/drivers/modules/irmc/test_bios.py
index 093bcdea4..5f7ee3274 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_bios.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_bios.py
@@ -87,10 +87,14 @@ class IRMCBIOSTestCase(test_common.BaseIRMCTest):
self.assertRaises(exception.UnsupportedDriverExtension,
task.driver.bios.factory_reset, task)
- @mock.patch.object(objects.BIOSSettingList, 'sync_node_setting')
- @mock.patch.object(objects.BIOSSettingList, 'create')
- @mock.patch.object(objects.BIOSSettingList, 'save')
- @mock.patch.object(objects.BIOSSettingList, 'delete')
+ @mock.patch.object(objects.BIOSSettingList, 'sync_node_setting',
+ autospec=True)
+ @mock.patch.object(objects.BIOSSettingList, 'create',
+ autospec=True)
+ @mock.patch.object(objects.BIOSSettingList, 'save',
+ autospec=True)
+ @mock.patch.object(objects.BIOSSettingList, 'delete',
+ autospec=True)
@mock.patch.object(irmc_bios.irmc.elcm, 'get_bios_settings',
autospec=True)
def test_cache_bios_settings(self, get_bios_settings_mock,
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_boot.py b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
index d8fdc2c52..0c97b9e89 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
@@ -1722,7 +1722,8 @@ class IRMCVirtualMediaBootWithVolumeTestCase(test_common.BaseIRMCTest):
self.assertRaises(exception.MissingParameterValue,
self._call_validate)
- @mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id')
+ @mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id',
+ autospec=True)
def test_prepare_ramdisk_skip(self, mock_nic, mock_viom,
check_share_fs_mounted_mock):
self._create_iscsi_resources()
@@ -1731,7 +1732,7 @@ class IRMCVirtualMediaBootWithVolumeTestCase(test_common.BaseIRMCTest):
task.driver.boot.prepare_ramdisk(task, {})
mock_nic.assert_not_called()
- @mock.patch.object(irmc_boot, '_cleanup_vmedia_boot')
+ @mock.patch.object(irmc_boot, '_cleanup_vmedia_boot', autospec=True)
def test_prepare_instance(self, mock_clean, mock_viom,
check_share_fs_mounted_mock):
mock_conf = self._create_mock_conf(mock_viom)
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_periodic_task.py b/ironic/tests/unit/drivers/modules/irmc/test_periodic_task.py
index 52230259d..6bda0fee6 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_periodic_task.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_periodic_task.py
@@ -50,7 +50,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
'key': 'value'
}]}
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
def test__query_raid_config_fgi_status_without_node(
self, report_mock):
mock_manager = mock.Mock()
@@ -60,7 +60,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
raid_object._query_raid_config_fgi_status(mock_manager, None)
self.assertEqual(0, report_mock.call_count)
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_without_raid_object(
self, mock_acquire, report_mock):
@@ -77,7 +77,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.context)
self.assertEqual(0, report_mock.call_count)
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_without_input(
self, mock_acquire, report_mock):
@@ -95,7 +95,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.context)
self.assertEqual(0, report_mock.call_count)
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_without_raid_config(
self, mock_acquire, report_mock):
@@ -110,7 +110,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.context)
self.assertEqual(0, report_mock.call_count)
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_without_fgi_status(
self, mock_acquire, report_mock):
@@ -132,7 +132,7 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.context)
self.assertEqual(0, report_mock.call_count)
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_other_clean_state(
self, mock_acquire, report_mock):
@@ -150,9 +150,11 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.context)
self.assertEqual(0, report_mock.call_count)
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status')
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status',
+ autospec=True)
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_completing_status(
self, mock_acquire, report_mock, fgi_mock, clean_fail_mock):
@@ -176,9 +178,11 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
fgi_mock.assert_called_once_with(report_mock.return_value,
self.node.uuid)
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status')
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status',
+ autospec=True)
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_with_clean_fail(
self, mock_acquire, report_mock, fgi_mock, clean_fail_mock):
@@ -198,15 +202,19 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
task.node.save()
task.driver.raid._query_raid_config_fgi_status(mock_manager,
self.context)
- clean_fail_mock.assert_called_once_with(task, fgi_status_dict)
+ clean_fail_mock.assert_called_once_with(mock.ANY, task,
+ fgi_status_dict)
report_mock.assert_called_once_with(task.node)
fgi_mock.assert_called_once_with(report_mock.return_value,
self.node.uuid)
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning')
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status')
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status',
+ autospec=True)
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_with_complete_cleaning(
self, mock_acquire, report_mock, fgi_mock, clean_fail_mock,
@@ -229,12 +237,15 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
report_mock.assert_called_once_with(task.node)
fgi_mock.assert_called_once_with(report_mock.return_value,
self.node.uuid)
- clean_mock.assert_called_once_with(task)
+ clean_mock.assert_called_once_with(mock.ANY, task)
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning')
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status')
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status',
+ autospec=True)
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_with_two_nodes_without_raid_config(
self, mock_acquire, report_mock, fgi_mock, clean_fail_mock,
@@ -259,12 +270,15 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
report_mock.assert_called_once_with(task.node)
fgi_mock.assert_called_once_with(report_mock.return_value,
self.node.uuid)
- clean_mock.assert_called_once_with(task)
+ clean_mock.assert_called_once_with(mock.ANY, task)
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning')
- @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status')
- @mock.patch.object(irmc_common, 'get_irmc_report')
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._resume_cleaning',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid.IRMCRAID._set_clean_failed',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_fgi_status',
+ autospec=True)
+ @mock.patch.object(irmc_common, 'get_irmc_report', autospec=True)
@mock.patch.object(task_manager, 'acquire', autospec=True)
def test__query_raid_config_fgi_status_with_two_nodes_with_fgi_status_none(
self, mock_acquire, report_mock, fgi_mock, clean_fail_mock,
@@ -291,5 +305,6 @@ class iRMCPeriodicTaskTestCase(test_common.BaseIRMCTest):
self.node_2.uuid),
mock.call(report_mock.return_value,
self.node_2.uuid)])
- clean_fail_mock.assert_called_once_with(task, fgi_status_dict)
- clean_mock.assert_called_once_with(task)
+ clean_fail_mock.assert_called_once_with(mock.ANY,
+ task, fgi_status_dict)
+ clean_mock.assert_called_once_with(mock.ANY, task)
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_power.py b/ironic/tests/unit/drivers/modules/irmc/test_power.py
index db335e941..c4142202c 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_power.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_power.py
@@ -118,7 +118,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_power_on_ok(
self,
attach_boot_iso_if_needed_mock,
@@ -152,7 +153,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_reboot_ok(
self,
attach_boot_iso_if_needed_mock,
@@ -171,7 +173,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_soft_reboot_ok(
self,
attach_boot_iso_if_needed_mock,
@@ -192,7 +195,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_soft_power_off_ok(self,
attach_boot_iso_if_needed_mock,
get_irmc_client_mock,
@@ -209,7 +213,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
@mock.patch.object(irmc_power, '_wait_power_state', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_invalid_target_state(
self,
attach_boot_iso_if_needed_mock,
@@ -227,7 +232,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_scci_exception(self,
attach_boot_iso_if_needed_mock,
get_irmc_client_mock,
@@ -250,7 +256,8 @@ class IRMCPowerInternalMethodsTestCase(test_common.BaseIRMCTest):
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,
autospec=True)
- @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
+ @mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed',
+ autospec=True)
def test__set_power_state_snmp_exception(self,
attach_boot_iso_if_needed_mock,
get_irmc_client_mock,
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_raid.py b/ironic/tests/unit/drivers/modules/irmc/test_raid.py
index 7698c256a..d95f09d16 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_raid.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_raid.py
@@ -138,8 +138,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
]
}
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test___fail_validation_with_none_raid_adapter_profile(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = None
@@ -158,8 +160,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test___fail_validation_without_raid_level(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -177,8 +181,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test___fail_validation_with_raid_level_is_none(self,
get_raid_adapter_mock,
get_physical_disk_mock):
@@ -198,8 +204,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_without_physical_disks(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = {
@@ -235,8 +243,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test___fail_validation_with_raid_level_outside_list(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -256,9 +266,12 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
task.node, target_raid_config['logical_disks'])
@mock.patch(
- 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_not_enough_valid_disks(
self, get_raid_adapter_mock, get_physical_disk_mock,
capacity_mock):
@@ -282,8 +295,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_physical_disk_insufficient(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -307,8 +322,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_physical_disk_not_enough_disks(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -331,8 +348,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_physical_disk_incorrect_valid_disks(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -358,8 +377,10 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_physical_disk_outside_valid_disks_1(
self, get_raid_adapter_mock, get_physical_disk_mock):
get_raid_adapter_mock.return_value = self.raid_adapter_profile
@@ -383,9 +404,12 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
task.node, target_raid_config['logical_disks'])
@mock.patch(
- 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_physical_disk_outside_valid_slots_2(
self, get_raid_adapter_mock, get_physical_disk_mock,
capacity_mock):
@@ -418,9 +442,12 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
task.node, target_raid_config['logical_disks'])
@mock.patch(
- 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk')
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ 'ironic.drivers.modules.irmc.raid._validate_logical_drive_capacity',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_physical_disk',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_duplicated_physical_disks(
self, get_raid_adapter_mock, get_physical_disk_mock,
capacity_mock):
@@ -452,7 +479,8 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_physical_disks,
task.node, target_raid_config['logical_disks'])
- @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._get_raid_adapter',
+ autospec=True)
def test__fail_validation_with_difference_physical_disks_type(
self, get_raid_adapter_mock):
get_raid_adapter_mock.return_value = {
@@ -573,11 +601,12 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
raid._validate_logical_drive_capacity,
disk, self.valid_disk_slots)
- @mock.patch('ironic.common.raid.update_raid_info')
- @mock.patch('ironic.drivers.modules.irmc.raid.client')
- def test__commit_raid_config_with_logical_drives(self, client_mock,
- update_raid_info_mock):
- client_mock.elcm.get_raid_adapter.return_value = {
+ @mock.patch('ironic.common.raid.update_raid_info', autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid.client.elcm.'
+ 'get_raid_adapter', autospec=True)
+ def test__commit_raid_config_with_logical_drives(
+ self, get_raid_adapter_mock, update_raid_info_mock):
+ get_raid_adapter_mock.return_value = {
"Server": {
"HWConfigurationIrmc": {
"Adapters": {
@@ -665,7 +694,7 @@ class IRMCRaidConfigurationInternalMethodsTestCase(test_common.BaseIRMCTest):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
raid._commit_raid_config(task)
- client_mock.elcm.get_raid_adapter.assert_called_once_with(
+ get_raid_adapter_mock.assert_called_once_with(
task.node.driver_info)
update_raid_info_mock.assert_called_once_with(
task.node, task.node.raid_config)
@@ -730,9 +759,12 @@ class IRMCRaidConfigurationTestCase(test_common.BaseIRMCTest):
self.assertRaises(exception.MissingParameterValue,
raid_configuration.create_configuration, task)
- @mock.patch('ironic.drivers.modules.irmc.raid._validate_physical_disks')
- @mock.patch('ironic.drivers.modules.irmc.raid._create_raid_adapter')
- @mock.patch('ironic.drivers.modules.irmc.raid._commit_raid_config')
+ @mock.patch('ironic.drivers.modules.irmc.raid._validate_physical_disks',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._create_raid_adapter',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._commit_raid_config',
+ autospec=True)
def test_create_raid_with_raid_1_and_0(self, commit_mock,
create_raid_mock, validation_mock):
expected_input = {
@@ -759,9 +791,12 @@ class IRMCRaidConfigurationTestCase(test_common.BaseIRMCTest):
task.node, expected_input['logical_disks'])
commit_mock.assert_called_once_with(task)
- @mock.patch('ironic.drivers.modules.irmc.raid._validate_physical_disks')
- @mock.patch('ironic.drivers.modules.irmc.raid._create_raid_adapter')
- @mock.patch('ironic.drivers.modules.irmc.raid._commit_raid_config')
+ @mock.patch('ironic.drivers.modules.irmc.raid._validate_physical_disks',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._create_raid_adapter',
+ autospec=True)
+ @mock.patch('ironic.drivers.modules.irmc.raid._commit_raid_config',
+ autospec=True)
def test_create_raid_with_raid_5_and_0(self, commit_mock,
create_raid_mock, validation_mock):
expected_input = {
@@ -788,7 +823,8 @@ class IRMCRaidConfigurationTestCase(test_common.BaseIRMCTest):
task.node, expected_input['logical_disks'])
commit_mock.assert_called_once_with(task)
- @mock.patch('ironic.drivers.modules.irmc.raid._delete_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._delete_raid_adapter',
+ autospec=True)
def test_delete_raid_configuration(self, delete_raid_mock):
with task_manager.acquire(self.context, self.node.uuid,
@@ -796,7 +832,8 @@ class IRMCRaidConfigurationTestCase(test_common.BaseIRMCTest):
task.driver.raid.delete_configuration(task)
delete_raid_mock.assert_called_once_with(task.node)
- @mock.patch('ironic.drivers.modules.irmc.raid._delete_raid_adapter')
+ @mock.patch('ironic.drivers.modules.irmc.raid._delete_raid_adapter',
+ autospec=True)
def test_delete_raid_configuration_return_cleared_raid_config(
self, delete_raid_mock):
with task_manager.acquire(self.context, self.node.uuid,
diff --git a/ironic/tests/unit/drivers/modules/test_console_utils.py b/ironic/tests/unit/drivers/modules/test_console_utils.py
index 752fa5fd1..3419abb4a 100644
--- a/ironic/tests/unit/drivers/modules/test_console_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_console_utils.py
@@ -23,6 +23,7 @@ import ipaddress
import os
import random
import signal
+import socket
import string
import subprocess
import tempfile
@@ -668,7 +669,7 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
def test_allocate_port_success(self, mock_verify, mock_ports):
self.config(port_range='10000:10001', group='console')
port = console_utils.acquire_port()
- mock_verify.assert_called_once_with(10000)
+ mock_verify.assert_called_once_with(10000, host=None)
self.assertEqual(port, 10000)
mock_ports.add.assert_called_once_with(10000)
@@ -679,7 +680,9 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
mock_verify.side_effect = (exception.Conflict, exception.Conflict,
None)
port = console_utils.acquire_port()
- verify_calls = [mock.call(10000), mock.call(10001), mock.call(10002)]
+ verify_calls = [mock.call(10000, host=None),
+ mock.call(10001, host=None),
+ mock.call(10002, host=None)]
mock_verify.assert_has_calls(verify_calls)
self.assertEqual(port, 10002)
mock_ports.add.assert_called_once_with(10002)
@@ -691,5 +694,39 @@ class ConsoleUtilsTestCase(db_base.DbTestCase):
mock_verify.side_effect = exception.Conflict
self.assertRaises(exception.NoFreeIPMITerminalPorts,
console_utils.acquire_port)
- verify_calls = [mock.call(p) for p in range(10000, 10005)]
+ verify_calls = [mock.call(p, host=None) for p in range(10000, 10005)]
mock_verify.assert_has_calls(verify_calls)
+
+ @mock.patch.object(socket, 'socket', autospec=True)
+ def test__verify_port_default(self, mock_socket):
+ self.config(host='localhost.localdomain')
+ mock_sock = mock.MagicMock()
+ mock_socket.return_value = mock_sock
+ console_utils._verify_port(10000)
+ mock_sock.bind.assert_called_once_with(('localhost.localdomain',
+ 10000))
+
+ @mock.patch.object(socket, 'socket', autospec=True)
+ def test__verify_port_hostname(self, mock_socket):
+ mock_sock = mock.MagicMock()
+ mock_socket.return_value = mock_sock
+ console_utils._verify_port(10000, host='localhost.localdomain')
+ mock_socket.assert_called_once_with()
+ mock_sock.bind.assert_called_once_with(('localhost.localdomain',
+ 10000))
+
+ @mock.patch.object(socket, 'socket', autospec=True)
+ def test__verify_port_ipv4(self, mock_socket):
+ mock_sock = mock.MagicMock()
+ mock_socket.return_value = mock_sock
+ console_utils._verify_port(10000, host='1.2.3.4')
+ mock_socket.assert_called_once_with()
+ mock_sock.bind.assert_called_once_with(('1.2.3.4', 10000))
+
+ @mock.patch.object(socket, 'socket', autospec=True)
+ def test__verify_port_ipv6(self, mock_socket):
+ mock_sock = mock.MagicMock()
+ mock_socket.return_value = mock_sock
+ console_utils._verify_port(10000, host='2001:dead:beef::1')
+ mock_socket.assert_called_once_with(socket.AF_INET6)
+ mock_sock.bind.assert_called_once_with(('2001:dead:beef::1', 10000))
diff --git a/ironic/tests/unit/drivers/modules/test_ipmitool.py b/ironic/tests/unit/drivers/modules/test_ipmitool.py
index e45aee26f..c13aae62a 100644
--- a/ironic/tests/unit/drivers/modules/test_ipmitool.py
+++ b/ironic/tests/unit/drivers/modules/test_ipmitool.py
@@ -2629,7 +2629,7 @@ class IPMIToolDriverTestCase(Base):
with task_manager.acquire(self.context,
self.node.uuid) as task:
port = ipmi._allocate_port(task)
- mock_acquire.assert_called_once_with()
+ mock_acquire.assert_called_once_with(host=None)
self.assertEqual(port, 1234)
info = task.node.driver_internal_info
self.assertEqual(info['allocated_ipmi_terminal_port'], 1234)
@@ -2959,6 +2959,7 @@ class IPMIToolSocatDriverTestCase(IPMIToolShellinaboxTestCase):
autospec=True)
def test_start_console_alloc_port(self, mock_stop, mock_start, mock_info,
mock_alloc):
+ self.config(socat_address='2001:dead:beef::1', group='console')
mock_start.return_value = None
mock_info.return_value = {'port': None}
mock_alloc.return_value = 1234
@@ -2970,7 +2971,7 @@ class IPMIToolSocatDriverTestCase(IPMIToolShellinaboxTestCase):
mock_start.assert_called_once_with(
self.console, {'port': 1234},
console_utils.start_socat_console)
- mock_alloc.assert_called_once_with(mock.ANY)
+ mock_alloc.assert_called_once_with(mock.ANY, host='2001:dead:beef::1')
@mock.patch.object(ipmi.IPMISocatConsole, '_get_ipmi_cmd', autospec=True)
@mock.patch.object(console_utils, 'start_socat_console',
diff --git a/ironic/tests/unit/objects/test_deployment.py b/ironic/tests/unit/objects/test_deployment.py
new file mode 100644
index 000000000..cb62fad8e
--- /dev/null
+++ b/ironic/tests/unit/objects/test_deployment.py
@@ -0,0 +1,117 @@
+# 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.
+
+from oslo_utils import uuidutils
+
+from ironic.common import exception
+from ironic import objects
+from ironic.tests.unit.db import base as db_base
+from ironic.tests.unit.objects import utils as obj_utils
+
+
+class TestDeploymentObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
+
+ def setUp(self):
+ super(TestDeploymentObject, self).setUp()
+ self.uuid = uuidutils.generate_uuid()
+ self.instance_info = {
+ 'image_source': 'http://source',
+ 'kernel': 'http://kernel',
+ 'ramdisk': 'http://ramdisk',
+ 'image_checksum': '1234',
+ 'root_device': {'size': 42},
+ }
+ self.node = obj_utils.create_test_node(
+ self.context,
+ provision_state='active',
+ instance_uuid=self.uuid,
+ instance_info=self.instance_info)
+
+ def _check(self, do):
+ self.assertEqual(self.uuid, do.uuid)
+ self.assertEqual(self.node.uuid, do.node_uuid)
+ self.assertEqual(self.context, do._context)
+ self.assertEqual('http://source', do.image_ref)
+ self.assertEqual('http://kernel', do.kernel_ref)
+ self.assertEqual('http://ramdisk', do.ramdisk_ref)
+ self.assertEqual('1234', do.image_checksum)
+ self.assertEqual({'size': 42}, do.root_device)
+
+ def test_get_by_uuid(self):
+ do = objects.Deployment.get_by_uuid(self.context, self.uuid)
+ self._check(do)
+
+ def test_get_by_node_uuid(self):
+ do = objects.Deployment.get_by_node_uuid(self.context, self.node.uuid)
+ self._check(do)
+
+ def test_not_found(self):
+ self.assertRaises(exception.InstanceNotFound,
+ objects.Deployment.get_by_uuid,
+ self.context, uuidutils.generate_uuid())
+ self.assertRaises(exception.NodeNotFound,
+ objects.Deployment.get_by_node_uuid,
+ self.context, uuidutils.generate_uuid())
+
+ def test_create(self):
+ do = objects.Deployment(self.context)
+ do.node_uuid = self.node.uuid
+ do.image_ref = 'new-image'
+ do.create()
+ self.assertIsNotNone(do.uuid)
+
+ node = objects.Node.get_by_uuid(self.context, do.node_uuid)
+ self.assertEqual(do.uuid, node.instance_uuid)
+ self.assertEqual('new-image', node.instance_info['image_source'])
+ self.assertFalse(do.obj_what_changed())
+
+ def test_create_with_node(self):
+ do = objects.Deployment(self.context)
+ do.node_uuid = self.node.uuid
+ do.image_ref = 'new-image'
+ do.create(node=self.node)
+ self.assertIsNotNone(do.uuid)
+ self.assertEqual(do.uuid, self.node.instance_uuid)
+ self.assertEqual('new-image', self.node.instance_info['image_source'])
+ self.assertFalse(do.obj_what_changed())
+ self.assertFalse(self.node.obj_what_changed())
+
+ def test_destroy(self):
+ do = objects.Deployment(self.context)
+ do.node_uuid = self.node.uuid
+ do.image_ref = 'new-image'
+ do.create()
+ do.destroy()
+
+ node = objects.Node.get_by_uuid(self.context, do.node_uuid)
+ self.assertIsNone(node.instance_uuid)
+ self.assertEqual({}, node.instance_info)
+ self.assertFalse(do.obj_what_changed())
+
+ def test_destroy_with_node(self):
+ do = objects.Deployment(self.context)
+ do.node_uuid = self.node.uuid
+ do.image_ref = 'new-image'
+ do.create()
+ do.destroy(node=self.node)
+ self.assertIsNone(self.node.instance_uuid)
+ self.assertEqual({}, self.node.instance_info)
+ self.assertFalse(do.obj_what_changed())
+ self.assertFalse(self.node.obj_what_changed())
+
+ def test_refresh(self):
+ do = objects.Deployment.get_by_uuid(self.context, self.uuid)
+ do.node_uuid = None
+ do.image_source = 'updated'
+ do.refresh()
+ self._check(do)
+ self.assertFalse(do.obj_what_changed())
diff --git a/ironic/tests/unit/objects/test_objects.py b/ironic/tests/unit/objects/test_objects.py
index 1320d96d3..ffdf375fd 100644
--- a/ironic/tests/unit/objects/test_objects.py
+++ b/ironic/tests/unit/objects/test_objects.py
@@ -719,6 +719,7 @@ expected_object_fingerprints = {
'DeployTemplate': '1.1-4e30c8e9098595e359bb907f095bf1a9',
'DeployTemplateCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
'DeployTemplateCRUDPayload': '1.0-200857e7e715f58a5b6d6b700ab73a3b',
+ 'Deployment': '1.0-ff10ae028c5968f1596131d85d7f5f9d',
}
diff --git a/releasenotes/notes/add-ansible-python-interpreter-2035e0f23d407aaf.yaml b/releasenotes/notes/add-ansible-python-interpreter-2035e0f23d407aaf.yaml
index b653ec259..a8b865126 100644
--- a/releasenotes/notes/add-ansible-python-interpreter-2035e0f23d407aaf.yaml
+++ b/releasenotes/notes/add-ansible-python-interpreter-2035e0f23d407aaf.yaml
@@ -3,7 +3,7 @@ features:
- Adds option ``[ansible]default_python_interpreter`` to choose
the python interpreter that ansible uses on managed machines.
By default, ansible uses ``/usr/bin/python`` as interpreter, making the
- assumption that that path is always present on remote managed systems.
+ assumption that path is always present on remote managed systems.
This might not be always the case, for example in custom build
images or Python 3 native distributions.
With this option the operator has the ability to set the absolute
@@ -12,4 +12,4 @@ features:
The same interpreter will be used in all operations that use the
ansible deploy interface.
It is also possible to override the value set in the configuration for a
- node by passing ``ansible_python_interpreter`` in its ``driver_info``. \ No newline at end of file
+ node by passing ``ansible_python_interpreter`` in its ``driver_info``.
diff --git a/releasenotes/notes/fixes-get-boot-option-for-software-raid-baa2cffd95e1f624.yaml b/releasenotes/notes/fixes-get-boot-option-for-software-raid-baa2cffd95e1f624.yaml
index 849273ac8..f3c9fad45 100644
--- a/releasenotes/notes/fixes-get-boot-option-for-software-raid-baa2cffd95e1f624.yaml
+++ b/releasenotes/notes/fixes-get-boot-option-for-software-raid-baa2cffd95e1f624.yaml
@@ -3,4 +3,4 @@ fixes:
- |
Fixes a minor issue with ``get_boot_option`` logic that did not account
for Software RAID. This can erroniously cause the deployment to take the
- the incorrect deployment path and attempt to install a boot loader.
+ incorrect deployment path and attempt to install a boot loader.
diff --git a/releasenotes/notes/skip-power-sync-for-adoptfail-d2498f1a2e997ed7.yaml b/releasenotes/notes/skip-power-sync-for-adoptfail-d2498f1a2e997ed7.yaml
new file mode 100644
index 000000000..2bff91797
--- /dev/null
+++ b/releasenotes/notes/skip-power-sync-for-adoptfail-d2498f1a2e997ed7.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+ - |
+ Fixes the conductor so the power sync operations are not asserted for
+ nodes in the ``adopt failed`` state.
diff --git a/releasenotes/notes/socat-console-port-alloc-ipv6-26760f53f86209d0.yaml b/releasenotes/notes/socat-console-port-alloc-ipv6-26760f53f86209d0.yaml
new file mode 100644
index 000000000..f8087363b
--- /dev/null
+++ b/releasenotes/notes/socat-console-port-alloc-ipv6-26760f53f86209d0.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+ - |
+ Fixes the issue that port auto allocation for the socat console failed to
+ correctly identify the availablility of ports under IPv6 networks.
diff --git a/tox.ini b/tox.ini
index 3395e567d..de75a0021 100644
--- a/tox.ini
+++ b/tox.ini
@@ -134,7 +134,6 @@ per-file-ignores =
ironic/tests/unit/common/*:H210
ironic/tests/unit/drivers/modules/test_console_utils.py:H210
ironic/tests/unit/drivers/modules/ilo/*:H210
- ironic/tests/unit/drivers/modules/irmc/*:H210
[hacking]
import_exceptions = testtools.matchers, ironic.common.i18n