Merge "Remove inspector inspect interface"
This commit is contained in:
@@ -59,10 +59,6 @@ As usual with the ``noop`` management, enable the networking boot fallback:
|
||||
[pxe]
|
||||
enable_netboot_fallback = true
|
||||
|
||||
If using discovery, :ironic-inspector-doc:`configure discovery in
|
||||
ironic-inspector <user/usage.html#discovery>` with the default driver set
|
||||
to ``manual-management``.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Interface Supported Implementations
|
||||
``deploy`` ``direct``, ``ansible``, ``ramdisk``
|
||||
``firmware`` ``redfish``, ``no-firmware``
|
||||
``inspect`` ``idrac-redfish``,
|
||||
``inspector``, ``no-inspect``
|
||||
``agent``, ``no-inspect``
|
||||
``management`` ``idrac-redfish``
|
||||
``network`` ``flat``, ``neutron``, ``noop``
|
||||
``power`` ``redfish``, ``idrac-redfish``
|
||||
|
||||
@@ -145,7 +145,7 @@ The ``ilo`` hardware type supports following hardware interfaces:
|
||||
management engine.
|
||||
|
||||
* inspect
|
||||
Supports ``ilo`` and ``inspector``. The default is ``ilo``. They
|
||||
Supports ``ilo`` and ``agent``. The default is ``ilo``. They
|
||||
can be enabled by using the :oslo.config:option:`DEFAULT.enabled_inspect_interfaces` option
|
||||
in ``ironic.conf`` as given below:
|
||||
|
||||
@@ -153,11 +153,7 @@ The ``ilo`` hardware type supports following hardware interfaces:
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = ilo
|
||||
enabled_inspect_interfaces = ilo,inspector
|
||||
|
||||
.. note::
|
||||
:ironic-inspector-doc:`Ironic Inspector <>`
|
||||
needs to be configured to use ``inspector`` as the inspect interface.
|
||||
enabled_inspect_interfaces = ilo,agent
|
||||
|
||||
* management
|
||||
Supports only ``ilo``. It can be enabled by using the
|
||||
|
||||
@@ -65,14 +65,9 @@ hardware interfaces:
|
||||
The default is ``ipmitool-socat``.
|
||||
|
||||
* inspect
|
||||
Supports ``irmc``, ``inspector``, and ``no-inspect``.
|
||||
Supports ``irmc``, ``agent``, and ``no-inspect``.
|
||||
The default is ``irmc``.
|
||||
|
||||
.. note::
|
||||
:ironic-inspector-doc:`Ironic Inspector <>`
|
||||
needs to be present and configured to use ``inspector`` as the
|
||||
inspect interface.
|
||||
|
||||
* management
|
||||
Supports only ``irmc``.
|
||||
|
||||
@@ -100,7 +95,7 @@ interfaces enabled for ``irmc`` hardware type.
|
||||
enabled_boot_interfaces = irmc-virtual-media,irmc-pxe
|
||||
enabled_console_interfaces = ipmitool-socat,ipmitool-shellinabox,no-console
|
||||
enabled_deploy_interfaces = direct
|
||||
enabled_inspect_interfaces = irmc,inspector,no-inspect
|
||||
enabled_inspect_interfaces = irmc,agent,no-inspect
|
||||
enabled_management_interfaces = irmc
|
||||
enabled_network_interfaces = flat,neutron
|
||||
enabled_power_interfaces = irmc
|
||||
|
||||
@@ -385,8 +385,8 @@ Out-Of-Band inspection
|
||||
|
||||
The ``redfish`` hardware type can inspect the bare metal node by querying
|
||||
Redfish compatible BMC. This process is quick and reliable compared to the
|
||||
way the ``inspector`` hardware type works i.e. booting bare metal node
|
||||
into the introspection ramdisk.
|
||||
way the ``agent`` hardware type works i.e. booting bare metal node into
|
||||
the introspection ramdisk.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -48,9 +48,9 @@ node:
|
||||
Inspection
|
||||
----------
|
||||
|
||||
If using :ref:`in-band inspection`, you need to tell ironic or ironic-inspector
|
||||
not to power off nodes afterwards. Depending on the inspection mode (managed or
|
||||
unmanaged, with ironic-inspector or without), you need to configure two places.
|
||||
If using :ref:`in-band inspection`, you need to tell ironic not to power off
|
||||
nodes afterwards. Depending on the inspection mode (managed or unmanaged),
|
||||
you need to configure two places.
|
||||
In ``ironic.conf``:
|
||||
|
||||
.. code-block:: ini
|
||||
@@ -58,13 +58,6 @@ In ``ironic.conf``:
|
||||
[inspector]
|
||||
power_off = false
|
||||
|
||||
And in ``inspector.conf`` (if needed):
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[processing]
|
||||
power_off = false
|
||||
|
||||
Finally, you need to update the :ref:`inspection PXE/iPXE
|
||||
configuration <configure-unmanaged-inspection>` to include the
|
||||
``ipa-api-url`` kernel parameter, pointing at the **ironic** endpoint, in
|
||||
|
||||
@@ -18,9 +18,6 @@ There are three kinds of inspection supported by Bare Metal service:
|
||||
#. :doc:`In-band inspection </admin/inspection/index>` utilizing Ironic Python
|
||||
Agent to collect information.
|
||||
|
||||
#. :doc:`Older in-band inspection </admin/inspection/inspector>` implementation
|
||||
utilizing the ironic-inspector_ project. This is now **deprecated**.
|
||||
|
||||
The node should be in the ``manageable`` state before inspection is initiated.
|
||||
If it is in the ``enroll`` or ``available`` state, move it to ``manageable``
|
||||
first::
|
||||
@@ -31,8 +28,6 @@ Then inspection can be initiated using the following command::
|
||||
|
||||
baremetal node inspect <node_UUID>
|
||||
|
||||
.. _ironic-inspector: https://pypi.org/project/ironic-inspector
|
||||
|
||||
.. _capabilities-discovery:
|
||||
|
||||
Capabilities discovery
|
||||
|
||||
@@ -7,20 +7,11 @@ information directly from it. This process is more fragile and time-consuming
|
||||
than the out-of-band inspection, but it is not vendor-specific and works
|
||||
across a wide range of hardware.
|
||||
|
||||
In the 2023.2 "Bobcat" release series, Ironic received an experimental
|
||||
implementation of in-band inspection that does not require the separate
|
||||
ironic-inspector_ service.
|
||||
|
||||
.. note::
|
||||
The implementation described in this document is not 100% compatible with
|
||||
the previous one (based on ironic-inspector_). Check the documentation and
|
||||
the previous one (based on ironic-inspector). Check the documentation and
|
||||
the release notes for which features are currently available.
|
||||
|
||||
Use :doc:`inspector` for production deployments of Ironic 2023.2 or earlier
|
||||
releases.
|
||||
|
||||
.. _ironic-inspector: https://pypi.org/project/ironic-inspector
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
@@ -38,8 +29,6 @@ stored data, nor does it support the ``"scope"`` field.
|
||||
The scope field allowed a rule to be applied only to specific nodes with
|
||||
matching scope value rather than all nodes where conditions are met.
|
||||
|
||||
:ironic-inspector-doc:`Inspection rules <user/usage.html#introspection-rules>`
|
||||
|
||||
Inspection Rules
|
||||
----------------
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
Inspector Support
|
||||
=================
|
||||
|
||||
Ironic supports in-band inspection using the ironic-inspector_ project. This
|
||||
is the original in-band inspection implementation, which is being gradually
|
||||
phased out in favour of a similar implementation inside Ironic proper.
|
||||
|
||||
It is supported by all hardware types, and used by default, if enabled, by the
|
||||
``ipmi`` hardware type. The ``inspector`` *inspect* interface has to be
|
||||
enabled to use it:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_inspect_interfaces = inspector,no-inspect
|
||||
|
||||
If the ironic-inspector service is not registered in the service catalog, set
|
||||
the following option:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[inspector]
|
||||
endpoint_override = http://inspector.example.com:5050
|
||||
|
||||
In order to ensure that ports in Bare Metal service are synchronized with
|
||||
NIC ports on the node, the following settings in the ironic-inspector
|
||||
configuration file must be set:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[processing]
|
||||
add_ports = all
|
||||
keep_ports = present
|
||||
|
||||
Managed and unmanaged inspection
|
||||
--------------------------------
|
||||
|
||||
There are two modes of in-band inspection: *managed* inspection and *unmanaged*
|
||||
inspection. See :doc:`/admin/inspection/managed` for more details.
|
||||
|
||||
.. _ironic-inspector: https://pypi.org/project/ironic-inspector
|
||||
.. _python-ironicclient: https://pypi.org/project/python-ironicclient
|
||||
@@ -34,15 +34,6 @@ inspection specifically. This is where you can configure
|
||||
[inspector]
|
||||
extra_kernel_params = ipa-inspection-collectors=default,logs ipa-collect-lldp=1
|
||||
|
||||
For the callback URL the ironic-inspector endpoint from the service catalog is
|
||||
used. If you want to override the endpoint for callback only, set the following
|
||||
option:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[inspector]
|
||||
callback_endpoint_override = https://example.com/baremetal-introspection/v1/continue
|
||||
|
||||
For the built-in inspection, the bare metal API endpoint can be overridden
|
||||
instead:
|
||||
|
||||
|
||||
@@ -6,10 +6,6 @@ that is responsible for :ref:`unmanaged-inspection`. Running it allows
|
||||
this dnsmasq instance to co-exist with the OpenStack Networking service's DHCP
|
||||
server on the same physical network.
|
||||
|
||||
.. warning::
|
||||
The PXE filter service is currently experimental. For a production grade
|
||||
solution, please stay with ironic-inspector for the time being.
|
||||
|
||||
How it works?
|
||||
-------------
|
||||
|
||||
|
||||
@@ -220,13 +220,7 @@ There is a possibility of utilizing
|
||||
a machine, however Grub2's capabilities for booting a machine are extremely
|
||||
limited when compared to a tool like iPXE. It is also worth noting the bulk
|
||||
of Ironic's example configurations utilize iPXE, including whole activities
|
||||
like unmanaged hardware introspection with ironic-inspector.
|
||||
|
||||
For extra context, unmanaged introspection is when you ask ironic-inspector
|
||||
to inspect a machine *instead* of asking ironic. In other words, using
|
||||
``openstack baremetal introspection start <node>`` versus
|
||||
``baremetal node inspect <node>`` commands. This does require the
|
||||
:oslo.config:option:`inspector.require_managed_boot` setting be set to ``true``.
|
||||
like unmanaged hardware introspection with agent inspect interface.
|
||||
|
||||
Driver support for Deployment with Secure Boot
|
||||
----------------------------------------------
|
||||
|
||||
@@ -1192,11 +1192,10 @@ To remedy this, try setting ``[pxe_filter]sync_period`` to be less frequent,
|
||||
i.e. a larger value to enable conductors to have time between running syncs.
|
||||
|
||||
.. note::
|
||||
It is anticipated that as part of the 2024.1 release, Ironic will have
|
||||
this functionality also merged into Ironic directly as part of the
|
||||
merge of the ``ironic-inspector`` service into ``ironic`` itself. This
|
||||
merger will result in a slightly more performant implementation, which may
|
||||
necessitate re-evaluation and tuning of the ``[pxe_filter]sync_period``
|
||||
Ironic now has this functionality also merged into Ironic directly as part
|
||||
of the merge of the ``ironic-inspector`` service into ``ironic`` itself.
|
||||
This merger will result in a slightly more performant implementation, which
|
||||
may necessitate re-evaluation and tuning of the ``[pxe_filter]sync_period``
|
||||
parameter.
|
||||
|
||||
Some or all of my baremetal nodes disappeared! Help?!
|
||||
|
||||
@@ -71,7 +71,6 @@ openstackdocs_projects = [
|
||||
'cinder',
|
||||
'glance',
|
||||
'ironic',
|
||||
'ironic-inspector',
|
||||
'ironic-lib',
|
||||
'ironic-neutron-agent',
|
||||
'ironic-python-agent',
|
||||
@@ -88,7 +87,6 @@ openstackdocs_projects = [
|
||||
'osprofiler',
|
||||
'os-traits',
|
||||
'python-ironicclient',
|
||||
'python-ironic-inspector-client',
|
||||
'python-openstackclient',
|
||||
'swift',
|
||||
]
|
||||
|
||||
@@ -40,11 +40,9 @@ which are developed by the same community.
|
||||
.. seealso::
|
||||
|
||||
* :bifrost-doc:`Bifrost Documentation <>`
|
||||
* :ironic-inspector-doc:`Ironic Inspector Documentation <>`
|
||||
* :ironic-lib-doc:`Ironic Lib Documentation <>`
|
||||
* :ironic-python-agent-doc:`Ironic Python Agent (IPA) Documentation <>`
|
||||
* :python-ironicclient-doc:`Ironic Client Documentation <>`
|
||||
* :python-ironic-inspector-client-doc:`Ironic Inspector Client Documentation <>`
|
||||
|
||||
Adding New Features
|
||||
===================
|
||||
|
||||
@@ -528,11 +528,6 @@ it is a misconfiguration of the deployment.
|
||||
Configuring unmanaged in-band inspection
|
||||
----------------------------------------
|
||||
|
||||
This section must be followed if you intend to use :ref:`unmanaged-inspection`
|
||||
without ironic-inspector. For ironic-inspector support, check `its installation
|
||||
guide
|
||||
<https://docs.openstack.org/ironic-inspector/latest/install/index.html#configuration>`_.
|
||||
|
||||
With PXE
|
||||
~~~~~~~~
|
||||
|
||||
|
||||
@@ -93,15 +93,13 @@ deploy
|
||||
inspect
|
||||
implements fetching hardware information from nodes. Can be implemented
|
||||
out-of-band (via contacting the node's BMC) or in-band (via booting
|
||||
a ramdisk on a node). The latter implementation is called ``inspector``
|
||||
and uses a separate service called
|
||||
:ironic-inspector-doc:`ironic-inspector <>`. Example:
|
||||
a ramdisk on a node, mainly achieved by the agent interface).
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
enabled_hardware_types = ipmi,ilo,irmc
|
||||
enabled_inspect_interfaces = ilo,irmc,inspector
|
||||
enabled_inspect_interfaces = ilo,irmc,agent
|
||||
|
||||
See :doc:`/admin/inspection` for more details.
|
||||
management
|
||||
|
||||
@@ -40,8 +40,8 @@ ironic-conductor
|
||||
|
||||
ironic-python-agent
|
||||
A python service which is run in a temporary ramdisk to provide
|
||||
ironic-conductor and ironic-inspector services with remote access, in-band
|
||||
hardware control, and hardware introspection.
|
||||
ironic-conductor with remote access, in-band hardware control, and hardware
|
||||
introspection.
|
||||
|
||||
ironic-novncproxy
|
||||
A python service which proxies graphical consoles from hosts using the
|
||||
@@ -125,10 +125,6 @@ additional functionality:
|
||||
Horizon dashboard, providing graphical interface (GUI) for the Bare Metal
|
||||
API.
|
||||
|
||||
:ironic-inspector-doc:`ironic-inspector <>`
|
||||
An associated service which performs in-band hardware introspection by
|
||||
PXE booting unregistered hardware into the ironic-python-agent ramdisk.
|
||||
|
||||
diskimage-builder_
|
||||
A related project to help facilitate the creation of ramdisks and machine
|
||||
images, such as those running the ironic-python-agent.
|
||||
|
||||
@@ -104,8 +104,6 @@ Configuring ironic-conductor service
|
||||
* ``[glance]`` - to access the OpenStack Image service
|
||||
* ``[swift]`` - to access the OpenStack Object Storage service
|
||||
* ``[cinder]`` - to access the OpenStack Block Storage service
|
||||
* ``[inspector]`` - to access the OpenStack Bare Metal Introspection
|
||||
service
|
||||
* ``[service_catalog]`` - a special section holding credentials
|
||||
the Bare Metal service will use to discover its own API URL endpoint
|
||||
as registered in the OpenStack Identity service catalog.
|
||||
|
||||
@@ -23,10 +23,9 @@ which disk it should pick for the deployment. The list of supported hints is:
|
||||
|
||||
.. note::
|
||||
A node's 'local_gb' property is often set to a value 1 GiB less than the
|
||||
actual disk size to account for partitioning (this is how DevStack, TripleO
|
||||
and Ironic Inspector work, to name a few). However, in this case ``size``
|
||||
should be the actual size. For example, for a 128 GiB disk ``local_gb``
|
||||
will be 127, but size hint will be 128.
|
||||
actual disk size to account for partitioning. However, in this case
|
||||
``size`` should be the actual size. For example, for a 128 GiB disk
|
||||
``local_gb`` will be 127, but size hint will be 128.
|
||||
|
||||
* wwn (STRING): unique storage identifier and typically mapping to a device.
|
||||
This can be a single device, or a SAN storage controller,
|
||||
|
||||
@@ -28,13 +28,10 @@ components participating in the bare metal provisioning:
|
||||
|
||||
* The :glance-doc:`Image service <>` provides images for bare metal instances.
|
||||
|
||||
The following services can be optionally used by the Bare Metal service:
|
||||
The following service can be optionally used by the Bare Metal service:
|
||||
|
||||
* The :cinder-doc:`Volume service <>` provides volumes to boot bare metal instances from.
|
||||
|
||||
* The :ironic-inspector-doc:`Bare Metal Introspection service <>` simplifies enrolling new bare metal
|
||||
machines by conducting in-band introspection.
|
||||
|
||||
Node roles
|
||||
----------
|
||||
|
||||
@@ -189,17 +186,6 @@ The following components of the Bare Metal service are installed on a
|
||||
|
||||
The :ironic-neutron-agent-doc:`ironic-neutron-agent <>` service should be started as well.
|
||||
|
||||
* If the Bare Metal introspection is used, its ``ironic-inspector`` process
|
||||
has to be installed on all *controllers*. Each such process works as both
|
||||
Bare Metal Introspection API and conductor service. A load balancer should
|
||||
be used to spread the API load between *controllers*.
|
||||
|
||||
The API has to be served on the *control plane network*. Additionally,
|
||||
it has to be exposed to the *bare metal network* for the ramdisk callback
|
||||
API.
|
||||
|
||||
.. TODO(dtantsur): a nice picture to illustrate the above
|
||||
|
||||
Shared services
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import os
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic.common.i18n import _
|
||||
from ironic.conf import auth
|
||||
|
||||
|
||||
VALID_ADD_PORTS_VALUES = {
|
||||
@@ -62,21 +61,20 @@ opts = [
|
||||
'finishes. Ignored for nodes that have fast '
|
||||
'track mode enabled.')),
|
||||
cfg.StrOpt('callback_endpoint_override',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason=_('This option was used by inspector '
|
||||
'inspect interface, which was removed.'),
|
||||
help=_('endpoint to use as a callback for posting back '
|
||||
'introspection data when boot is managed by ironic. '
|
||||
'Standard keystoneauth options are used by default.')),
|
||||
# TODO(dtantsur): change the default to True when ironic-inspector is no
|
||||
# longer supported (and update the help string).
|
||||
cfg.BoolOpt('require_managed_boot', default=None,
|
||||
cfg.BoolOpt('require_managed_boot', default=True,
|
||||
help=_('require that the in-band inspection boot is fully '
|
||||
'managed by the node\'s boot interface. Set this to '
|
||||
'False if your installation has a separate (i)PXE boot '
|
||||
'environment for node discovery or unmanaged '
|
||||
'inspection. You may need to set it to False to '
|
||||
'inspect nodes that are not supported by boot '
|
||||
'interfaces (e.g. because they don\'t have ports). '
|
||||
'The default value depends on which inspect interface '
|
||||
'is used: inspector uses False, agent - True.')),
|
||||
'interfaces (e.g. because they don\'t have ports).')),
|
||||
cfg.StrOpt('add_ports',
|
||||
default='pxe',
|
||||
help=_('Which MAC addresses to add as ports during '
|
||||
@@ -203,9 +201,3 @@ def register_opts(conf):
|
||||
conf.register_opts(discovery_opts, group='auto_discovery')
|
||||
conf.register_opts(pxe_filter_opts, group='pxe_filter')
|
||||
conf.register_opts(inspection_rule_opts, group='inspection_rules')
|
||||
auth.register_auth_opts(conf, 'inspector',
|
||||
service_type='baremetal-introspection')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return auth.add_auth_opts(opts, service_type='baremetal-introspection')
|
||||
|
||||
@@ -37,7 +37,7 @@ _opts = [
|
||||
('healthcheck', ironic.conf.healthcheck.opts),
|
||||
('ilo', ironic.conf.ilo.opts),
|
||||
('inspection_rules', ironic.conf.inspector.inspection_rule_opts),
|
||||
('inspector', ironic.conf.inspector.list_opts()),
|
||||
('inspector', ironic.conf.inspector.opts),
|
||||
('inventory', ironic.conf.inventory.opts),
|
||||
('ipmi', ironic.conf.ipmi.opts),
|
||||
('irmc', ironic.conf.irmc.opts),
|
||||
|
||||
@@ -58,7 +58,7 @@ class GenericHardware(hardware_type.AbstractHardwareType):
|
||||
"""List of supported inspect interfaces."""
|
||||
# Inspector support should be the default if it's enabled by an
|
||||
# operator (implying that the service is installed).
|
||||
return [inspector.Inspector, inspector.AgentInspect, noop.NoInspect]
|
||||
return [inspector.AgentInspect, noop.NoInspect]
|
||||
|
||||
@property
|
||||
def supported_network_interfaces(self):
|
||||
|
||||
@@ -11,6 +11,5 @@
|
||||
# under the License.
|
||||
|
||||
from ironic.drivers.modules.inspector.agent import AgentInspect
|
||||
from ironic.drivers.modules.inspector.interface import Inspector
|
||||
|
||||
__all__ = ['AgentInspect', 'Inspector']
|
||||
__all__ = ['AgentInspect']
|
||||
|
||||
@@ -35,8 +35,6 @@ CONF = cfg.CONF
|
||||
class AgentInspect(common.Common):
|
||||
"""In-band inspection."""
|
||||
|
||||
default_require_managed_boot = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
enabled_hooks = [x.strip()
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""Client helper for ironic-inspector."""
|
||||
|
||||
from keystoneauth1 import exceptions as ks_exception
|
||||
import openstack
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import keystone
|
||||
from ironic.conf import CONF
|
||||
|
||||
|
||||
_INSPECTOR_SESSION = None
|
||||
|
||||
|
||||
def _get_inspector_session(**kwargs):
|
||||
global _INSPECTOR_SESSION
|
||||
if not _INSPECTOR_SESSION:
|
||||
if CONF.auth_strategy != 'keystone':
|
||||
# NOTE(dtantsur): using set_default instead of set_override because
|
||||
# the native keystoneauth option must have priority.
|
||||
CONF.set_default('auth_type', 'none', group='inspector')
|
||||
service_auth = keystone.get_auth('inspector')
|
||||
_INSPECTOR_SESSION = keystone.get_session('inspector',
|
||||
auth=service_auth,
|
||||
**kwargs)
|
||||
return _INSPECTOR_SESSION
|
||||
|
||||
|
||||
def get_client(context):
|
||||
"""Helper to get inspector client instance."""
|
||||
session = _get_inspector_session()
|
||||
# NOTE(dtantsur): openstacksdk expects config option groups to match
|
||||
# service name, but we use just "inspector".
|
||||
conf = dict(CONF)
|
||||
conf['ironic-inspector'] = conf.pop('inspector')
|
||||
# TODO(pas-ha) investigate possibility of passing user context here,
|
||||
# similar to what neutron/glance-related code does
|
||||
try:
|
||||
return openstack.connection.Connection(
|
||||
session=session,
|
||||
oslo_conf=conf).baremetal_introspection
|
||||
except ks_exception.DiscoveryFailure as exc:
|
||||
raise exception.ConfigInvalid(
|
||||
_("Could not contact ironic-inspector for version discovery: %s")
|
||||
% exc)
|
||||
@@ -10,13 +10,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Modules required to work with ironic_inspector:
|
||||
https://pypi.org/project/ironic-inspector
|
||||
"""
|
||||
|
||||
import threading
|
||||
from urllib import parse as urlparse
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
@@ -24,14 +17,11 @@ from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import periodics
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as cond_utils
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules import inspect_utils
|
||||
from ironic.drivers.modules.inspector import client
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -39,29 +29,6 @@ LOG = logging.getLogger(__name__)
|
||||
_IRONIC_MANAGES_BOOT = 'inspector_manage_boot'
|
||||
|
||||
|
||||
def _get_callback_endpoint(client):
|
||||
root = CONF.inspector.callback_endpoint_override or client.get_endpoint()
|
||||
if root == 'mdns':
|
||||
return root
|
||||
|
||||
parts = urlparse.urlsplit(root)
|
||||
|
||||
if utils.is_loopback(parts.hostname):
|
||||
raise exception.InvalidParameterValue(
|
||||
_('Loopback address %s cannot be used as an introspection '
|
||||
'callback URL') % parts.hostname)
|
||||
|
||||
# NOTE(dtantsur): the IPA side is quite picky about the exact format.
|
||||
if parts.path.endswith('/v1'):
|
||||
add = '/continue'
|
||||
else:
|
||||
add = '/v1/continue'
|
||||
|
||||
return urlparse.urlunsplit((parts.scheme, parts.netloc,
|
||||
parts.path.rstrip('/') + add,
|
||||
parts.query, parts.fragment))
|
||||
|
||||
|
||||
def tear_down_managed_boot(task, always_power_off=False):
|
||||
errors = []
|
||||
|
||||
@@ -167,21 +134,6 @@ def prepare_managed_inspection(task, endpoint):
|
||||
|
||||
class Common(base.InspectInterface):
|
||||
|
||||
default_require_managed_boot = False
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
if CONF.inspector.require_managed_boot is None:
|
||||
LOG.warning("The option [inspector]require_managed_boot will "
|
||||
"change its default value to True in the future. "
|
||||
"Set it to an explicit boolean value to avoid a "
|
||||
"potential breakage.")
|
||||
|
||||
def _require_managed_boot(self):
|
||||
return (CONF.inspector.require_managed_boot
|
||||
if CONF.inspector.require_managed_boot is not None
|
||||
else self.default_require_managed_boot)
|
||||
|
||||
def get_properties(self):
|
||||
"""Return the properties of the interface.
|
||||
|
||||
@@ -198,7 +150,7 @@ class Common(base.InspectInterface):
|
||||
:raises: UnsupportedDriverExtension
|
||||
"""
|
||||
utils.parse_kernel_params(CONF.inspector.extra_kernel_params)
|
||||
if self._require_managed_boot():
|
||||
if CONF.inspector.require_managed_boot:
|
||||
ironic_manages_boot(task, raise_exc=True)
|
||||
|
||||
def inspect_hardware(self, task):
|
||||
@@ -217,7 +169,7 @@ class Common(base.InspectInterface):
|
||||
' on node %s.', task.node.uuid)
|
||||
|
||||
manage_boot = ironic_manages_boot(
|
||||
task, raise_exc=self._require_managed_boot())
|
||||
task, raise_exc=CONF.inspector.require_managed_boot)
|
||||
|
||||
utils.set_node_nested_field(task.node, 'driver_internal_info',
|
||||
_IRONIC_MANAGES_BOOT, manage_boot)
|
||||
@@ -252,139 +204,6 @@ class Common(base.InspectInterface):
|
||||
cond_utils.node_power_action(task, next_state)
|
||||
|
||||
|
||||
class Inspector(Common):
|
||||
"""In-band inspection via ironic-inspector project."""
|
||||
|
||||
def _start_managed_inspection(self, task):
|
||||
"""Start inspection with boot managed by ironic."""
|
||||
cli = client.get_client(task.context)
|
||||
endpoint = _get_callback_endpoint(cli)
|
||||
prepare_managed_inspection(task, endpoint)
|
||||
cli.start_introspection(task.node.uuid, manage_boot=False)
|
||||
self._power_on_or_reboot(task)
|
||||
|
||||
def _start_unmanaged_inspection(self, task):
|
||||
"""Call to inspector to start inspection."""
|
||||
# NOTE(cid): spawning a short-lived daemon OS thread so that
|
||||
# we can release a lock as soon as possible and allow
|
||||
# ironic-inspector to operate on the node.
|
||||
threading.Thread(target=_start_inspection,
|
||||
args=(task.node.uuid, task.context),
|
||||
daemon=True).start()
|
||||
|
||||
def abort(self, task):
|
||||
"""Abort hardware inspection.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
"""
|
||||
node_uuid = task.node.uuid
|
||||
LOG.debug('Aborting inspection for node %(uuid)s using '
|
||||
'ironic-inspector', {'uuid': node_uuid})
|
||||
client.get_client(task.context).abort_introspection(node_uuid)
|
||||
if inspect_utils.clear_lookup_addresses(task.node):
|
||||
task.node.save()
|
||||
|
||||
@periodics.node_periodic(
|
||||
purpose='checking hardware inspection status',
|
||||
spacing=CONF.inspector.status_check_period,
|
||||
filters={'provision_state': states.INSPECTWAIT},
|
||||
)
|
||||
def _periodic_check_result(self, task, manager, context):
|
||||
"""Periodic task checking results of inspection."""
|
||||
if isinstance(task.driver.inspect, self.__class__):
|
||||
_check_status(task)
|
||||
|
||||
def continue_inspection(self, task, inventory, plugin_data=None):
|
||||
"""Continue in-band hardware inspection.
|
||||
|
||||
This implementation simply defers to ironic-inspector. It only exists
|
||||
to simplify the transition to Ironic-native in-band inspection.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param inventory: hardware inventory from the node.
|
||||
:param plugin_data: optional plugin-specific data.
|
||||
"""
|
||||
cli = client.get_client(task.context)
|
||||
endpoint = _get_callback_endpoint(cli)
|
||||
data = dict(plugin_data, inventory=inventory) # older format
|
||||
task.process_event('wait')
|
||||
task.downgrade_lock()
|
||||
cli.post(endpoint, json=data)
|
||||
return states.INSPECTWAIT
|
||||
|
||||
|
||||
def _start_inspection(node_uuid, context):
|
||||
"""Call to inspector to start inspection."""
|
||||
try:
|
||||
client.get_client(context).start_introspection(node_uuid)
|
||||
except Exception as exc:
|
||||
LOG.error('Error contacting ironic-inspector for inspection of node '
|
||||
'%(node)s: %(cls)s: %(err)s',
|
||||
{'node': node_uuid, 'cls': type(exc).__name__, 'err': exc})
|
||||
# NOTE(dtantsur): if acquire fails our last option is to rely on
|
||||
# timeout
|
||||
lock_purpose = 'recording hardware inspection error'
|
||||
with task_manager.acquire(context, node_uuid,
|
||||
purpose=lock_purpose) as task:
|
||||
error = _('Failed to start inspection: %s') % exc
|
||||
inspection_error_handler(task, error)
|
||||
else:
|
||||
LOG.info('Node %s was sent to inspection to ironic-inspector',
|
||||
node_uuid)
|
||||
|
||||
|
||||
def _check_status(task):
|
||||
"""Check inspection status from inspector for node given by a task."""
|
||||
node = task.node
|
||||
if node.provision_state != states.INSPECTWAIT:
|
||||
return
|
||||
if not isinstance(task.driver.inspect, Inspector):
|
||||
return
|
||||
|
||||
LOG.debug('Calling to inspector to check status of node %s',
|
||||
task.node.uuid)
|
||||
|
||||
try:
|
||||
inspector_client = client.get_client(task.context)
|
||||
status = inspector_client.get_introspection(node.uuid)
|
||||
except Exception:
|
||||
# NOTE(dtantsur): get_status should not normally raise
|
||||
# let's assume it's a transient failure and retry later
|
||||
LOG.exception('Unexpected exception while getting '
|
||||
'inspection status for node %s, will retry later',
|
||||
node.uuid)
|
||||
return
|
||||
|
||||
if not status.error and not status.is_finished:
|
||||
return
|
||||
|
||||
# If the inspection has finished or failed, we need to update the node, so
|
||||
# upgrade our lock to an exclusive one.
|
||||
task.upgrade_lock()
|
||||
node = task.node
|
||||
|
||||
inspect_utils.clear_lookup_addresses(node)
|
||||
|
||||
if status.error:
|
||||
LOG.error('Inspection failed for node %(uuid)s with error: %(err)s',
|
||||
{'uuid': node.uuid, 'err': status.error})
|
||||
error = _('ironic-inspector inspection failed: %s') % status.error
|
||||
inspection_error_handler(task, error)
|
||||
elif status.is_finished:
|
||||
clean_up(task)
|
||||
if CONF.inventory.data_backend == 'none':
|
||||
LOG.debug('Inspection data storage is disabled, the data will '
|
||||
'not be saved for node %s', node.uuid)
|
||||
return
|
||||
introspection_data = inspector_client.get_introspection_data(
|
||||
node.uuid, processed=True)
|
||||
# TODO(dtantsur): having no inventory is an abnormal state, handle it.
|
||||
inventory = introspection_data.pop('inventory', {})
|
||||
inspect_utils.store_inspection_data(node, inventory,
|
||||
introspection_data,
|
||||
task.context)
|
||||
|
||||
|
||||
def clean_up(task, finish=True, always_power_off=False):
|
||||
errors = tear_down_managed_boot(task, always_power_off=always_power_off)
|
||||
if errors:
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
# 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 unittest import mock
|
||||
|
||||
from keystoneauth1 import exceptions as ks_exception
|
||||
import openstack
|
||||
|
||||
from ironic.common import context
|
||||
from ironic.common import exception
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers.modules.inspector import client
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
|
||||
|
||||
@mock.patch('ironic.common.keystone.get_auth', autospec=True,
|
||||
return_value=mock.sentinel.auth)
|
||||
@mock.patch('ironic.common.keystone.get_session', autospec=True,
|
||||
return_value=mock.sentinel.session)
|
||||
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
|
||||
class GetClientTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GetClientTestCase, self).setUp()
|
||||
# NOTE(pas-ha) force-reset global inspector session object
|
||||
client._INSPECTOR_SESSION = None
|
||||
self.context = context.RequestContext(global_request_id='global')
|
||||
|
||||
def test_get_client(self, mock_conn, mock_session, mock_auth):
|
||||
client.get_client(self.context)
|
||||
mock_conn.assert_called_once_with(
|
||||
session=mock.sentinel.session,
|
||||
oslo_conf=mock.ANY)
|
||||
self.assertEqual(1, mock_auth.call_count)
|
||||
self.assertEqual(1, mock_session.call_count)
|
||||
|
||||
def test_get_client_standalone(self, mock_conn, mock_session, mock_auth):
|
||||
self.config(auth_strategy='noauth')
|
||||
client.get_client(self.context)
|
||||
self.assertEqual('none', CONF.inspector.auth_type)
|
||||
mock_conn.assert_called_once_with(
|
||||
session=mock.sentinel.session,
|
||||
oslo_conf=mock.ANY)
|
||||
self.assertEqual(1, mock_auth.call_count)
|
||||
self.assertEqual(1, mock_session.call_count)
|
||||
|
||||
def test_get_client_connection_problem(
|
||||
self, mock_conn, mock_session, mock_auth):
|
||||
mock_conn.side_effect = ks_exception.DiscoveryFailure("")
|
||||
self.assertRaises(exception.ConfigInvalid,
|
||||
client.get_client, self.context)
|
||||
mock_conn.assert_called_once_with(
|
||||
session=mock.sentinel.session,
|
||||
oslo_conf=mock.ANY)
|
||||
self.assertEqual(1, mock_auth.call_count)
|
||||
self.assertEqual(1, mock_session.call_count)
|
||||
@@ -1,607 +0,0 @@
|
||||
# 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 unittest import mock
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conf import CONF
|
||||
from ironic.drivers.modules import inspect_utils
|
||||
from ironic.drivers.modules.inspector import client
|
||||
from ironic.drivers.modules.inspector import interface as inspector
|
||||
from ironic.drivers.modules.redfish import utils as redfish_utils
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
|
||||
class BaseTestCase(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(BaseTestCase, self).setUp()
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
inspect_interface='inspector')
|
||||
self.iface = inspector.Inspector()
|
||||
self.task = mock.MagicMock(spec=task_manager.TaskManager)
|
||||
self.task.context = self.context
|
||||
self.task.shared = False
|
||||
self.task.node = self.node
|
||||
self.task.driver = mock.Mock(
|
||||
spec=['boot', 'network', 'inspect', 'power', 'management'],
|
||||
inspect=self.iface)
|
||||
self.driver = self.task.driver
|
||||
|
||||
|
||||
class CommonFunctionsTestCase(BaseTestCase):
|
||||
def test_validate_ok(self):
|
||||
self.iface.validate(self.task)
|
||||
|
||||
def test_get_properties(self):
|
||||
res = self.iface.get_properties()
|
||||
self.assertEqual({}, res)
|
||||
|
||||
def test_get_callback_endpoint(self):
|
||||
for catalog_endp in ['http://192.168.0.42:5050',
|
||||
'http://192.168.0.42:5050/v1',
|
||||
'http://192.168.0.42:5050/']:
|
||||
client = mock.Mock()
|
||||
client.get_endpoint.return_value = catalog_endp
|
||||
self.assertEqual('http://192.168.0.42:5050/v1/continue',
|
||||
inspector._get_callback_endpoint(client))
|
||||
|
||||
def test_get_callback_endpoint_override(self):
|
||||
CONF.set_override('callback_endpoint_override', 'http://url',
|
||||
group='inspector')
|
||||
client = mock.Mock()
|
||||
self.assertEqual('http://url/v1/continue',
|
||||
inspector._get_callback_endpoint(client))
|
||||
self.assertFalse(client.get_endpoint.called)
|
||||
|
||||
def test_get_callback_endpoint_mdns(self):
|
||||
CONF.set_override('callback_endpoint_override', 'mdns',
|
||||
group='inspector')
|
||||
client = mock.Mock()
|
||||
self.assertEqual('mdns', inspector._get_callback_endpoint(client))
|
||||
self.assertFalse(client.get_endpoint.called)
|
||||
|
||||
def test_get_callback_endpoint_no_loopback(self):
|
||||
client = mock.Mock()
|
||||
client.get_endpoint.return_value = 'http://127.0.0.1:5050'
|
||||
self.assertRaisesRegex(exception.InvalidParameterValue, 'Loopback',
|
||||
inspector._get_callback_endpoint, client)
|
||||
|
||||
|
||||
@mock.patch.object(client, 'get_client', autospec=True)
|
||||
class InspectHardwareTestCase(BaseTestCase):
|
||||
def test_validate_ok(self, mock_client):
|
||||
self.iface.validate(self.task)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_validate_require_managed_boot(self, mock_get_system,
|
||||
mock_create_ports_if_not_exist,
|
||||
mock_client):
|
||||
CONF.set_override('require_managed_boot', True, group='inspector')
|
||||
self.driver.boot.validate_inspection.side_effect = (
|
||||
exception.UnsupportedDriverExtension(''))
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
self.iface.validate, self.task)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_unmanaged_ok(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_client):
|
||||
self.driver.boot.validate_inspection.side_effect = (
|
||||
exception.UnsupportedDriverExtension(''))
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid)
|
||||
self.assertFalse(self.driver.boot.prepare_ramdisk.called)
|
||||
self.assertFalse(self.driver.network.add_inspection_network.called)
|
||||
self.assertFalse(self.driver.power.reboot.called)
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
@mock.patch.object(task_manager, 'acquire', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_unmanaged_error(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_acquire, mock_client):
|
||||
mock_acquire.return_value.__enter__.return_value = self.task
|
||||
self.driver.boot.validate_inspection.side_effect = (
|
||||
exception.UnsupportedDriverExtension(''))
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
mock_introspect.side_effect = RuntimeError('boom')
|
||||
self.iface.inspect_hardware(self.task)
|
||||
mock_introspect.assert_called_once_with(self.node.uuid)
|
||||
self.assertIn('boom', self.task.node.last_error)
|
||||
self.task.process_event.assert_called_once_with('fail')
|
||||
self.assertFalse(self.driver.boot.prepare_ramdisk.called)
|
||||
self.assertFalse(self.driver.network.add_inspection_network.called)
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_require_managed_boot(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_client):
|
||||
CONF.set_override('require_managed_boot', True, group='inspector')
|
||||
self.driver.boot.validate_inspection.side_effect = (
|
||||
exception.UnsupportedDriverExtension(''))
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
self.iface.inspect_hardware, self.task)
|
||||
self.assertFalse(mock_introspect.called)
|
||||
self.assertFalse(self.driver.boot.prepare_ramdisk.called)
|
||||
self.assertFalse(self.driver.network.add_inspection_network.called)
|
||||
self.assertFalse(self.driver.power.reboot.called)
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_ok(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_client):
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.power.set_power_state.assert_has_calls([
|
||||
mock.call(self.task, states.POWER_OFF, timeout=None),
|
||||
mock.call(self.task, states.POWER_ON, timeout=None),
|
||||
])
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_force_dhcp(self,
|
||||
mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_client):
|
||||
# Enable the new toggle
|
||||
CONF.set_override('force_dhcp', True, group='inspector')
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
# Ensure both DHCP on all interfaces and LLDP collection are present
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
'ipa-collect-lldp': '1',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_custom_params(self, mock_get_system,
|
||||
mock_create_ports_if_not_exist,
|
||||
mock_client):
|
||||
CONF.set_override('extra_kernel_params',
|
||||
'ipa-inspection-collectors=default,logs '
|
||||
'ipa-collect-dhcp=1 something',
|
||||
group='inspector')
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.iface.validate(self.task)
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
'ipa-inspection-collectors': 'default,logs',
|
||||
'ipa-collect-dhcp': '1',
|
||||
'something': None,
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.power.set_power_state.assert_has_calls([
|
||||
mock.call(self.task, states.POWER_OFF, timeout=None),
|
||||
mock.call(self.task, states.POWER_ON, timeout=None),
|
||||
])
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.deploy_utils.get_ironic_api_url',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_fast_track(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_ironic_url,
|
||||
mock_client):
|
||||
CONF.set_override('fast_track', True, group='deploy')
|
||||
CONF.set_override('extra_kernel_params',
|
||||
'ipa-inspection-collectors=default,logs '
|
||||
'ipa-collect-dhcp=1',
|
||||
group='inspector')
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_ironic_url.return_value = 'http://192.169.0.42:6385'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.iface.validate(self.task)
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
'ipa-inspection-collectors': 'default,logs',
|
||||
'ipa-collect-dhcp': '1',
|
||||
'ipa-api-url': 'http://192.169.0.42:6385',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.power.set_power_state.assert_has_calls([
|
||||
mock.call(self.task, states.POWER_OFF, timeout=None),
|
||||
mock.call(self.task, states.POWER_ON, timeout=None),
|
||||
])
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.deploy_utils.get_ironic_api_url',
|
||||
autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_fast_track_via_driver_info(
|
||||
self, mock_create_ports_if_not_exist, mock_get_system,
|
||||
mock_ironic_url, mock_client):
|
||||
CONF.set_override('extra_kernel_params',
|
||||
'ipa-inspection-collectors=default,logs '
|
||||
'ipa-collect-dhcp=1',
|
||||
group='inspector')
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_ironic_url.return_value = 'http://192.169.0.42:6385'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.task.node.driver_info = {'fast_track': True}
|
||||
self.iface.validate(self.task)
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
'ipa-inspection-collectors': 'default,logs',
|
||||
'ipa-collect-dhcp': '1',
|
||||
'ipa-api-url': 'http://192.169.0.42:6385',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.power.set_power_state.assert_has_calls([
|
||||
mock.call(self.task, states.POWER_OFF, timeout=None),
|
||||
mock.call(self.task, states.POWER_ON, timeout=None),
|
||||
])
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
|
||||
@mock.patch.object(task_manager, 'acquire', autospec=True)
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_error(self, mock_get_system,
|
||||
mock_create_ports_if_not_exist, mock_acquire,
|
||||
mock_client):
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_acquire.return_value.__enter__.return_value = self.task
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
mock_introspect.side_effect = RuntimeError('boom')
|
||||
self.assertRaises(exception.HardwareInspectionFailure,
|
||||
self.iface.inspect_hardware, self.task)
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.assertIn('boom', self.task.node.last_error)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
self.driver.power.set_power_state.assert_called_with(
|
||||
self.task, 'power off', timeout=None)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
def test_managed_disable_power_off(self, mock_create_ports_if_not_exist,
|
||||
mock_get_system, mock_client):
|
||||
endpoint = 'http://192.169.0.42:5050/v1'
|
||||
mock_client.return_value.get_endpoint.return_value = endpoint
|
||||
mock_introspect = mock_client.return_value.start_introspection
|
||||
self.node.disable_power_off = True
|
||||
self.assertEqual(states.INSPECTWAIT,
|
||||
self.iface.inspect_hardware(self.task))
|
||||
mock_introspect.assert_called_once_with(self.node.uuid,
|
||||
manage_boot=False)
|
||||
self.driver.boot.prepare_ramdisk.assert_called_once_with(
|
||||
self.task, ramdisk_params={
|
||||
'ipa-inspection-callback-url': endpoint + '/continue',
|
||||
})
|
||||
self.driver.network.add_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.power.reboot.assert_called_once_with(
|
||||
self.task, timeout=None)
|
||||
self.driver.power.set_power_state.assert_not_called()
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
|
||||
|
||||
class TearDownManagedInspectionTestCase(BaseTestCase):
|
||||
|
||||
def test_unmanaged(self):
|
||||
inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
def test_unmanaged_force_power_off(self):
|
||||
inspector.tear_down_managed_boot(self.task, always_power_off=True)
|
||||
|
||||
self.assertFalse(self.driver.network.remove_inspection_network.called)
|
||||
self.assertFalse(self.driver.boot.clean_up_ramdisk.called)
|
||||
self.driver.power.set_power_state.assert_called_once_with(
|
||||
self.task, 'power off', timeout=None)
|
||||
|
||||
def test_managed(self):
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.save()
|
||||
|
||||
inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
self.driver.power.set_power_state.assert_called_once_with(
|
||||
self.task, 'power off', timeout=None)
|
||||
|
||||
def test_managed_no_power_off(self):
|
||||
CONF.set_override('power_off', False, group='inspector')
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.save()
|
||||
|
||||
inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
def test_managed_no_power_off_on_fast_track(self):
|
||||
CONF.set_override('fast_track', True, group='deploy')
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.save()
|
||||
|
||||
inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
self.assertFalse(self.driver.power.set_power_state.called)
|
||||
|
||||
def test_managed_disable_power_off(self):
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.disable_power_off = True
|
||||
self.node.save()
|
||||
|
||||
inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
self.driver.power.reboot.assert_called_once_with(
|
||||
self.task, timeout=None)
|
||||
|
||||
def _test_clean_up_failed(self):
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.save()
|
||||
|
||||
result = inspector.tear_down_managed_boot(self.task)
|
||||
|
||||
self.assertIn("boom", result[0])
|
||||
|
||||
def test_boot_clean_up_failed(self):
|
||||
self.driver.boot.clean_up_ramdisk.side_effect = RuntimeError('boom')
|
||||
|
||||
self._test_clean_up_failed()
|
||||
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
|
||||
def test_network_clean_up_failed(self):
|
||||
self.driver.network.remove_inspection_network.side_effect = \
|
||||
RuntimeError('boom')
|
||||
|
||||
self._test_clean_up_failed()
|
||||
|
||||
self.driver.network.remove_inspection_network.assert_called_once_with(
|
||||
self.task)
|
||||
self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
|
||||
|
||||
|
||||
@mock.patch.object(client, 'get_client', autospec=True)
|
||||
class CheckStatusTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(CheckStatusTestCase, self).setUp()
|
||||
self.node.provision_state = states.INSPECTWAIT
|
||||
|
||||
def test_not_inspecting(self, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
self.node.provision_state = states.MANAGEABLE
|
||||
inspector._check_status(self.task)
|
||||
self.assertFalse(mock_get.called)
|
||||
|
||||
def test_not_check_inspecting(self, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
self.node.provision_state = states.INSPECTING
|
||||
inspector._check_status(self.task)
|
||||
self.assertFalse(mock_get.called)
|
||||
|
||||
def test_not_inspector(self, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
self.task.driver.inspect = object()
|
||||
inspector._check_status(self.task)
|
||||
self.assertFalse(mock_get.called)
|
||||
|
||||
def test_not_finished(self, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=False,
|
||||
error=None,
|
||||
spec=['is_finished', 'error'])
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
self.assertFalse(self.task.process_event.called)
|
||||
|
||||
def test_exception_ignored(self, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.side_effect = RuntimeError('boom')
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
self.assertFalse(self.task.process_event.called)
|
||||
|
||||
@mock.patch.object(inspector, 'tear_down_managed_boot', autospec=True)
|
||||
def test_status_ok(self, mock_tear_down, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error=None,
|
||||
spec=['is_finished', 'error'])
|
||||
mock_tear_down.return_value = []
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
self.task.process_event.assert_called_once_with('done')
|
||||
mock_tear_down.assert_called_once_with(
|
||||
self.task, always_power_off=False)
|
||||
|
||||
@mock.patch.object(inspector, 'tear_down_managed_boot', autospec=True)
|
||||
def test_status_error(self, mock_tear_down, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error='boom',
|
||||
spec=['is_finished', 'error'])
|
||||
mock_tear_down.return_value = []
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
self.task.process_event.assert_called_once_with('fail')
|
||||
self.assertIn('boom', self.node.last_error)
|
||||
mock_tear_down.assert_called_once_with(self.task)
|
||||
|
||||
@mock.patch.object(inspector, 'tear_down_managed_boot', autospec=True)
|
||||
def test_status_clean_up_failed(self, mock_tear_down, mock_client):
|
||||
utils.set_node_nested_field(self.node, 'driver_internal_info',
|
||||
'inspector_manage_boot', True)
|
||||
self.node.save()
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error=None,
|
||||
spec=['is_finished', 'error'])
|
||||
mock_tear_down.return_value = ["boom"]
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
self.task.process_event.assert_called_once_with('fail')
|
||||
self.assertIn('boom', self.node.last_error)
|
||||
mock_tear_down.assert_called_once_with(
|
||||
self.task, always_power_off=False)
|
||||
|
||||
@mock.patch.object(inspect_utils, 'store_inspection_data', autospec=True)
|
||||
def test_status_ok_store_inventory(self, mock_store_data, mock_client):
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error=None,
|
||||
spec=['is_finished', 'error'])
|
||||
fake_inventory = {"cpu": "amd"}
|
||||
fake_plugin_data = {"disks": [{"name": "/dev/vda"}]}
|
||||
fake_introspection_data = dict(fake_plugin_data,
|
||||
inventory=fake_inventory)
|
||||
mock_get_data = mock_client.return_value.get_introspection_data
|
||||
mock_get_data.return_value = fake_introspection_data
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
mock_get_data.assert_called_once_with(self.node.uuid, processed=True)
|
||||
mock_store_data.assert_called_once_with(self.node,
|
||||
fake_inventory,
|
||||
fake_plugin_data,
|
||||
self.task.context)
|
||||
|
||||
def test_status_ok_store_inventory_nostore(self, mock_client):
|
||||
CONF.set_override('data_backend', 'none', group='inventory')
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error=None,
|
||||
spec=['is_finished', 'error'])
|
||||
mock_get_data = mock_client.return_value.get_introspection_data
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
mock_get_data.assert_not_called()
|
||||
|
||||
def test_status_error_dont_store_inventory(self, mock_client):
|
||||
CONF.set_override('data_backend', 'database',
|
||||
group='inventory')
|
||||
mock_get = mock_client.return_value.get_introspection
|
||||
mock_get.return_value = mock.Mock(is_finished=True,
|
||||
error='boom',
|
||||
spec=['is_finished', 'error'])
|
||||
mock_get_data = mock_client.return_value.get_introspection_data
|
||||
inspector._check_status(self.task)
|
||||
mock_get.assert_called_once_with(self.node.uuid)
|
||||
mock_get_data.assert_not_called()
|
||||
|
||||
|
||||
@mock.patch.object(client, 'get_client', autospec=True)
|
||||
class InspectHardwareAbortTestCase(BaseTestCase):
|
||||
def test_abort_ok(self, mock_client):
|
||||
mock_abort = mock_client.return_value.abort_introspection
|
||||
self.iface.abort(self.task)
|
||||
mock_abort.assert_called_once_with(self.node.uuid)
|
||||
|
||||
def test_abort_error(self, mock_client):
|
||||
mock_abort = mock_client.return_value.abort_introspection
|
||||
mock_abort.side_effect = RuntimeError('boom')
|
||||
self.assertRaises(RuntimeError, self.iface.abort, self.task)
|
||||
|
||||
mock_abort.assert_called_once_with(self.node.uuid)
|
||||
@@ -38,7 +38,7 @@ class IDRACHardwareTestCase(db_base.DbTestCase):
|
||||
enabled_management_interfaces=['idrac-redfish'],
|
||||
enabled_power_interfaces=['idrac-redfish', 'redfish'],
|
||||
enabled_inspect_interfaces=[
|
||||
'idrac-redfish', 'inspector',
|
||||
'idrac-redfish', 'agent',
|
||||
'no-inspect'],
|
||||
enabled_network_interfaces=['flat', 'neutron', 'noop'],
|
||||
enabled_raid_interfaces=[
|
||||
@@ -99,10 +99,10 @@ class IDRACHardwareTestCase(db_base.DbTestCase):
|
||||
|
||||
def test_override_with_inspector(self):
|
||||
node = obj_utils.create_test_node(self.context, driver='idrac',
|
||||
inspect_interface='inspector')
|
||||
inspect_interface='agent')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self._validate_interfaces(task.driver,
|
||||
inspect=inspector.Inspector)
|
||||
inspect=inspector.AgentInspect)
|
||||
|
||||
def test_override_with_raid(self):
|
||||
for iface, impl in [('agent', agent.AgentRAID),
|
||||
|
||||
@@ -49,7 +49,7 @@ class ManualManagementHardwareTestCase(db_base.DbTestCase):
|
||||
self.assertIsInstance(task.driver.raid, noop.NoRAID)
|
||||
|
||||
def test_supported_interfaces(self):
|
||||
self.config(enabled_inspect_interfaces=['inspector', 'no-inspect'],
|
||||
self.config(enabled_inspect_interfaces=['agent', 'no-inspect'],
|
||||
enabled_deploy_interfaces=['direct', 'custom-agent'],
|
||||
enabled_raid_interfaces=['agent'])
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
@@ -62,7 +62,8 @@ class ManualManagementHardwareTestCase(db_base.DbTestCase):
|
||||
self.assertIsInstance(task.driver.power, fake.FakePower)
|
||||
self.assertIsInstance(task.driver.boot, pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy, agent.CustomAgentDeploy)
|
||||
self.assertIsInstance(task.driver.inspect, inspector.Inspector)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
inspector.AgentInspect)
|
||||
self.assertIsInstance(task.driver.raid, agent.AgentRAID)
|
||||
|
||||
def test_get_properties(self):
|
||||
|
||||
@@ -70,10 +70,10 @@ class IloHardwareTestCase(db_base.DbTestCase):
|
||||
noop.NoRescue)
|
||||
|
||||
def test_override_with_inspector(self):
|
||||
self.config(enabled_inspect_interfaces=['inspector', 'ilo'])
|
||||
self.config(enabled_inspect_interfaces=['agent', 'ilo'])
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='ilo',
|
||||
inspect_interface='inspector',
|
||||
inspect_interface='agent',
|
||||
raid_interface='agent',
|
||||
vendor_interface='no-vendor')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
@@ -84,7 +84,7 @@ class IloHardwareTestCase(db_base.DbTestCase):
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
agent.AgentDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
inspector.Inspector)
|
||||
inspector.AgentInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
ilo.management.IloManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
@@ -122,7 +122,7 @@ class IloHardwareTestCase(db_base.DbTestCase):
|
||||
ilo.vendor.VendorPassthru)
|
||||
|
||||
def test_override_with_agent_rescue(self):
|
||||
self.config(enabled_inspect_interfaces=['inspector', 'ilo'])
|
||||
self.config(enabled_inspect_interfaces=['agent', 'ilo'])
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='ilo',
|
||||
rescue_interface='agent',
|
||||
|
||||
@@ -73,10 +73,10 @@ class IRMCHardwareTestCase(db_base.DbTestCase):
|
||||
irmc_bios.IRMCBIOS)
|
||||
|
||||
def test_override_with_inspector(self, check_share_fs_mounted_mock):
|
||||
self.config(enabled_inspect_interfaces=['inspector', 'irmc'])
|
||||
self.config(enabled_inspect_interfaces=['agent', 'irmc'])
|
||||
node = obj_utils.create_test_node(
|
||||
self.context, driver='irmc',
|
||||
inspect_interface='inspector',
|
||||
inspect_interface='agent',
|
||||
raid_interface='agent')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
@@ -86,7 +86,7 @@ class IRMCHardwareTestCase(db_base.DbTestCase):
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
agent.AgentDeploy)
|
||||
self.assertIsInstance(task.driver.inspect,
|
||||
inspector.Inspector)
|
||||
inspector.AgentInspect)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
irmc.management.IRMCManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
|
||||
@@ -98,7 +98,6 @@ agent = "ironic.drivers.modules.inspector:AgentInspect"
|
||||
fake = "ironic.drivers.modules.fake:FakeInspect"
|
||||
idrac-redfish = "ironic.drivers.modules.drac.inspect:DracRedfishInspect"
|
||||
ilo = "ironic.drivers.modules.ilo.inspect:IloInspect"
|
||||
inspector = "ironic.drivers.modules.inspector:Inspector"
|
||||
irmc = "ironic.drivers.modules.irmc.inspect:IRMCInspect"
|
||||
no-inspect = "ironic.drivers.modules.noop:NoInspect"
|
||||
redfish = "ironic.drivers.modules.redfish.inspect:RedfishInspect"
|
||||
|
||||
10
releasenotes/notes/remove-inspector-17f1f289a017e8cc.yaml
Normal file
10
releasenotes/notes/remove-inspector-17f1f289a017e8cc.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The deprecated ``inspector`` inspect interface has been removed. Use
|
||||
the ``agent`` interface instead.
|
||||
|
||||
See `migration guide
|
||||
<https://docs.openstack.org/ironic/latest/admin/inspection/migration.html>`_
|
||||
to learn how existing deployments with ironic-inspector can be migrated to
|
||||
a new architecture with the built-in inspection feature.
|
||||
Reference in New Issue
Block a user