diff --git a/doc/source/deploy/drivers.rst b/doc/source/deploy/drivers.rst index 4e74f5d087..55bf35f56d 100644 --- a/doc/source/deploy/drivers.rst +++ b/doc/source/deploy/drivers.rst @@ -31,13 +31,6 @@ DRAC with PXE deploy ``/etc/ironic/ironic.conf`` - Install python-dracclient package -AMT driver ----------- - -.. toctree:: - :maxdepth: 1 - - ../drivers/amt SNMP driver ----------- @@ -89,24 +82,6 @@ Cisco UCS driver ../drivers/ucs -Wake-On-Lan driver ------------------- - -.. toctree:: - :maxdepth: 1 - - ../drivers/wol - - -iBoot driver ------------- - -.. toctree:: - :maxdepth: 1 - - ../drivers/iboot - - CIMC driver ----------- @@ -132,3 +107,16 @@ XenServer ssh driver :maxdepth: 1 ../drivers/xenserver + + +Unsupported drivers +------------------- + +The following drivers were declared as unsupported in ironic Newton release +and as of Ocata release they are removed form ironic: + +- AMT driver - available as part of ironic-staging-drivers_ +- iBoot driver - available as part of ironic-staging-drivers_ +- Wake-On-Lan driver - available as part of ironic-staging-drivers_ + +.. _ironic-staging-drivers: http://ironic-staging-drivers.readthedocs.io diff --git a/doc/source/dev/code-contribution-guide.rst b/doc/source/dev/code-contribution-guide.rst index 9dd0ace4fa..2bbb611be2 100644 --- a/doc/source/dev/code-contribution-guide.rst +++ b/doc/source/dev/code-contribution-guide.rst @@ -211,7 +211,7 @@ Agent driver attributes: These are only some fields in use. Other vendor drivers might expose more ``driver_internal_info`` properties, please check their development documentation and/or module docstring for details. It is important for developers to make sure these properties follow the precedent of prefixing their - variable names with a specific interface name (e.g., iboot_bar, amt_xyz), so as to minimize or avoid + variable names with a specific interface name (e.g., ilo_bar, drac_xyz), so as to minimize or avoid any conflicts between interfaces. diff --git a/doc/source/drivers/amt.rst b/doc/source/drivers/amt.rst deleted file mode 100644 index c4ec1afbdf..0000000000 --- a/doc/source/drivers/amt.rst +++ /dev/null @@ -1,89 +0,0 @@ -.. _amt: - -=========== -AMT drivers -=========== - -Overview -======== -AMT (Active Management Technology) drivers extend Ironic's range to the -desktop. AMT/vPro is widely used in desktops to remotely control their power, -similar to IPMI in servers. - -AMT drivers use WS-MAN protocol to interact with AMT clients. -They work on AMT 7.0/8.0/9.0. AMT 7.0 was released in 2010, so AMT drivers -should work on most PCs with vPro. - -There are two AMT drivers: - -* ``pxe_amt`` uses AMT for power management and deploys the user image over - iSCSI from the conductor - -* ``agent_amt`` uses AMT for power management and deploys the user image - directly to the node via HTTP. - -Set up your environment -======================= -A detailed reference is available here, and a short guide follows below: - - https://software.intel.com/en-us/articles/intel-active-management-technology-start-here-guide-intel-amt-9#4.2 - -* Set up AMT Client - - * Choose a system which supports Intel AMT / vPro. Desktop and laptop systems - that support this can often be identified by looking at the "Intel" tag for - the word ``vPro``. - - * During boot, press Ctrl+P to enter Intel MEBx management. - - * Reset password -- default is ``admin``. The new password must contain at - least one upper case letter, one lower case letter, one digit and one - special character, and be at least eight characters. - - * Go to Intel AMT Configuration: - - * Enable all features under SOL/IDER/KVM section - - * Select User Consent and choose None (No password is needed) - - * Select Network Setup section and set IP - - * Activate Network Access - - * MEBx Exit - - * Restart and enable PXE boot in bios - -* Install ``openwsman`` on servers where ``ironic-conductor`` is running: - - * Fedora/RHEL: ``openwsman-python``. - - * Ubuntu: ``python-openwsman``'s most recent version is 2.4.3 which - is enough. - - * Or build it yourself from: https://github.com/Openwsman/openwsman - -* Enable the ``pxe_amt`` or ``agent_amt`` driver by adding it to the - configuration option ``enabled_drivers`` (typically located at - ``/etc/ironic/ironic.conf``) and restart the ``ironic-conductor`` - process:: - - service ironic-conductor restart - -* Enroll an AMT node - -* Specify these driver_info properties for the node: ``amt_password``, - ``amt_address``, and ``amt_username`` - -* Boot an instance - -.. note:: - It is recommended that nodes using the pxe_amt driver be deployed with the - `local boot`_ option. This is because the AMT firmware currently has no - support for setting a persistent boot device. Nodes deployed without the - `local boot`_ option could fail to boot if they are restarted outside of - Ironic's control (I.E. rebooted by a local user) because the node will - not attempt to PXE / network boot the kernel, using `local boot`_ solves this - known issue. - -.. _`local boot`: http://docs.openstack.org/project-install-guide/baremetal/draft/advanced.html#local-boot-with-partition-images diff --git a/doc/source/drivers/iboot.rst b/doc/source/drivers/iboot.rst deleted file mode 100644 index e6ba6b65a4..0000000000 --- a/doc/source/drivers/iboot.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. _IBOOT: - -============ -iBoot driver -============ - -Overview -======== -The iBoot power driver enables you to take advantage of power cycle -management of nodes using Dataprobe iBoot devices over the DxP protocol. - -Drivers -======= - -There are two iboot drivers: - -* The ``pxe_iboot`` driver uses iBoot to control the power state of the - node, PXE/iPXE technology for booting and the iSCSI methodology for - deploying the node. - -* The ``agent_iboot`` driver uses iBoot to control the power state of the - node, PXE/iPXE technology for booting and the Ironic Python Agent for - deploying an image to the node. - -Requirements -~~~~~~~~~~~~ - -* ``python-iboot`` library should be installed - https://github.com/darkip/python-iboot - -Tested platforms -~~~~~~~~~~~~~~~~ - -* iBoot-G2 - -Configuring and enabling the driver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1. Add ``pxe_iboot`` and/or ``agent_iboot`` to the list of ``enabled_drivers`` - in */etc/ironic/ironic.conf*. For example:: - - [DEFAULT] - ... - enabled_drivers = pxe_iboot,agent_iboot - -2. Restart the Ironic conductor service:: - - service ironic-conductor restart - -Registering a node with the iBoot driver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Nodes configured for the iBoot driver should have the ``driver`` property -set to ``pxe_iboot`` or ``agent_iboot``. - -The following configuration values are also required in ``driver_info``: - -- ``iboot_address``: The IP address of the iBoot PDU. -- ``iboot_username``: User name used for authentication. -- ``iboot_password``: Password used for authentication. - -In addition, there are optional properties in ``driver_info``: - -- ``iboot_port``: iBoot PDU port. Defaults to 9100. -- ``iboot_relay_id``: iBoot PDU relay ID. This option is useful in order - to support multiple nodes attached to a single PDU. Defaults to 1. - -The following sequence of commands can be used to enroll a node with -the iBoot driver. - -1. Create node:: - - ironic node-create -d pxe_iboot -i iboot_username= -i iboot_password= -i iboot_address=
- -References -========== -.. [1] iBoot-G2 official documentation - http://dataprobe.com/support_iboot-g2.html diff --git a/doc/source/drivers/wol.rst b/doc/source/drivers/wol.rst deleted file mode 100644 index 6e3c67ef62..0000000000 --- a/doc/source/drivers/wol.rst +++ /dev/null @@ -1,128 +0,0 @@ -.. _WOL: - -================== -Wake-On-Lan driver -================== - -Overview -======== - -Wake-On-Lan is a standard that allows a computer to be powered on by a -network message. This is widely available and doesn't require any fancy -hardware to work with [1]_. - -The Wake-On-Lan driver is a **testing** driver not meant for -production. And useful for users that wants to try Ironic with real -bare metal instead of virtual machines. - -It's important to note that Wake-On-Lan is only capable of powering on -the machine. When power off is called the driver won't take any action -and will just log a message, the power off require manual intervention -to be performed. - -Also, since Wake-On-Lan does not offer any means to determine the current -power state of the machine, the driver relies on the power state set in -the Ironic database. Any calls to the API to get the power state of the -node will return the value from the Ironic's database. - - -Drivers -======= - -pxe_wol -^^^^^^^ - -Overview -~~~~~~~~ - -The ``pxe_wol`` driver uses the Wake-On-Lan technology to control the -power state, PXE/iPXE technology for booting and the iSCSI methodology -for deploying the node. - -Requirements -~~~~~~~~~~~~ - -* Wake-On-Lan should be enabled in the BIOS - -Configuring and Enabling the driver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1. Add ``pxe_wol`` to the list of ``enabled_drivers`` in - */etc/ironic/ironic.conf*. For example:: - - [DEFAULT] - ... - enabled_drivers = pxe_ipmitool,pxe_wol - -2. Restart the Ironic conductor service:: - - service ironic-conductor restart - -Registering a node with the Wake-On-Lan driver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Nodes configured for Wake-On-Lan driver should have the ``driver`` -property set to ``pxe_wol``. - -The node should have at least one port registered with it because the -Wake-On-Lan driver will use the MAC address of the ports to create the -magic packet [2]_. - -The following configuration values are optional and can be added to the -node's ``driver_info`` as needed to match the network configuration: - -- ``wol_host``: The broadcast IP address; defaults to - **255.255.255.255**. -- ``wol_port``: The destination port; defaults to **9**. - -.. note:: - Say the ``ironic-conductor`` is connected to more than one network and - the node you are trying to wake up is in the ``192.0.2.0/24`` range. The - ``wol_host`` configuration should be set to **192.0.2.255** (the - broadcast IP) so the packets will get routed correctly. - -The following sequence of commands can be used to enroll a node with -the Wake-On-Lan driver. - -1. Create node:: - - ironic node-create -d pxe_wol [-i wol_host= [ -i - wol_port=]] - -The above command ``ironic node-create`` will return UUID of the node, -which is the value of *$NODE* in the following command. - -2. Associate port with the node created:: - - ironic port-create -n $NODE -a - - -agent_wol -^^^^^^^^^ - -Overview -~~~~~~~~ - -The ``agent_wol`` driver uses the Wake-On-Lan technology to control the -power state, PXE/iPXE technology for booting and the Ironic Python Agent -for deploying the node. - -Additional requirements -~~~~~~~~~~~~~~~~~~~~~~~ - -* Boot device order should be set to "PXE, DISK" in the BIOS setup - -* BIOS must try next boot device if PXE boot failed - -* Automated cleaning should be disabled, see :ref:`automated_cleaning` - -* Node should be powered off before start of deploy - -Configuration steps are the same as for ``pxe_wol`` driver, replace "pxe_wol" -with "agent_wol". - - -References -========== -.. [1] Wake-On-Lan - https://en.wikipedia.org/wiki/Wake-on-LAN -.. [2] Magic packet - https://en.wikipedia.org/wiki/Wake-on-LAN#Sending_the_magic_packet diff --git a/driver-requirements.txt b/driver-requirements.txt index b5a418fdc7..930040b8bd 100644 --- a/driver-requirements.txt +++ b/driver-requirements.txt @@ -14,16 +14,6 @@ python-seamicroclient>=0.4.0 UcsSdk==0.8.2.2 python-dracclient>=0.1.0 -# The amt driver imports a python module called "pywsman", but this does not -# exist on pypi. -# It is installed by the openwsman-python (on RH) or python-openwsman (on deb) -# package, from https://github.com/Openwsman/openwsman/blob/master/bindings/python/Makefile.am#L29 -# There is *also* a "wsman" module on pypi ... but I think that's the wrong one. - -# The iboot driver does not seem to have any available packages or pip modules, -# but the source is available here: -# https://github.com/darkip/python-iboot - # 'pxe_vbox' and 'agent_vbox' drivers require pyremotevbox library. # Refer documentation on how to install and configure this: # http://docs.openstack.org/developer/ironic/drivers/vbox.html diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index ab0d663541..e080f47972 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -566,34 +566,6 @@ #deploy_logs_swift_days_to_expire = 30 -[amt] - -# -# From ironic -# - -# Protocol used for AMT endpoint (string value) -# Allowed values: http, https -#protocol = http - -# Time interval (in seconds) for successive awake call to AMT -# interface, this depends on the IdleTimeout setting on AMT -# interface. AMT Interface will go to sleep after 60 seconds -# of inactivity by default. IdleTimeout=0 means AMT will not -# go to sleep at all. Setting awake_interval=0 will disable -# awake call. (integer value) -# Minimum value: 0 -#awake_interval = 60 - -# Maximum number of times to attempt an AMT operation, before -# failing (integer value) -#max_attempts = 3 - -# Amount of time (in seconds) to wait, before retrying an AMT -# operation (integer value) -#action_wait = 10 - - [api] # @@ -1358,25 +1330,6 @@ #username = -[iboot] - -# -# From ironic -# - -# Maximum retries for iBoot operations (integer value) -#max_retry = 3 - -# Time (in seconds) between retry attempts for iBoot -# operations (integer value) -#retry_interval = 1 - -# Time (in seconds) to sleep between when rebooting (powering -# off and on again). (integer value) -# Minimum value: 0 -#reboot_delay = 5 - - [ilo] # diff --git a/ironic/common/exception.py b/ironic/common/exception.py index b0e0c3d04b..e832252a79 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -430,15 +430,6 @@ class IPMIFailure(IronicException): _msg_fmt = _("IPMI call failed: %(cmd)s.") -class AMTConnectFailure(IronicException): - _msg_fmt = _("Failed to connect to AMT service. This could be caused " - "by the wrong amt_address or bad network environment.") - - -class AMTFailure(IronicException): - _msg_fmt = _("AMT call failed: %(cmd)s.") - - class MSFTOCSClientApiException(IronicException): _msg_fmt = _("MSFT OCS call failed.") @@ -659,10 +650,6 @@ class UcsConnectionError(IronicException): "%(node)s. Reason: %(error)s") -class WolOperationError(IronicException): - pass - - class ImageUploadFailed(IronicException): _msg_fmt = _("Failed to upload %(image_name)s image to web server " "%(web_server)s, reason: %(reason)s") diff --git a/ironic/conf/__init__.py b/ironic/conf/__init__.py index 505c7bba1f..6456ddc6ce 100644 --- a/ironic/conf/__init__.py +++ b/ironic/conf/__init__.py @@ -16,7 +16,6 @@ from oslo_config import cfg from ironic.conf import agent -from ironic.conf import amt from ironic.conf import api from ironic.conf import audit from ironic.conf import cisco @@ -28,7 +27,6 @@ from ironic.conf import deploy from ironic.conf import dhcp from ironic.conf import drac from ironic.conf import glance -from ironic.conf import iboot from ironic.conf import ilo from ironic.conf import inspector from ironic.conf import ipmi @@ -50,7 +48,6 @@ from ironic.conf import virtualbox CONF = cfg.CONF agent.register_opts(CONF) -amt.register_opts(CONF) api.register_opts(CONF) audit.register_opts(CONF) cisco.register_opts(CONF) @@ -62,7 +59,6 @@ deploy.register_opts(CONF) drac.register_opts(CONF) dhcp.register_opts(CONF) glance.register_opts(CONF) -iboot.register_opts(CONF) ilo.register_opts(CONF) inspector.register_opts(CONF) ipmi.register_opts(CONF) diff --git a/ironic/conf/amt.py b/ironic/conf/amt.py deleted file mode 100644 index 152727d531..0000000000 --- a/ironic/conf/amt.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2016 Intel Corporation -# Copyright (c) 2012 NTT DOCOMO, INC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -from ironic.common.i18n import _ - - -opts = [ - cfg.StrOpt('protocol', - default='http', - choices=['http', 'https'], - help=_('Protocol used for AMT endpoint')), - cfg.IntOpt('awake_interval', - default=60, - min=0, - help=_('Time interval (in seconds) for successive awake call ' - 'to AMT interface, this depends on the IdleTimeout ' - 'setting on AMT interface. AMT Interface will go to ' - 'sleep after 60 seconds of inactivity by default. ' - 'IdleTimeout=0 means AMT will not go to sleep at all. ' - 'Setting awake_interval=0 will disable awake call.')), - cfg.IntOpt('max_attempts', - default=3, - help=_('Maximum number of times to attempt an AMT operation, ' - 'before failing')), - cfg.IntOpt('action_wait', - default=10, - help=_('Amount of time (in seconds) to wait, before retrying ' - 'an AMT operation')) -] - - -def register_opts(conf): - conf.register_opts(opts, group='amt') diff --git a/ironic/conf/iboot.py b/ironic/conf/iboot.py deleted file mode 100644 index 305fba3951..0000000000 --- a/ironic/conf/iboot.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2016 Intel Corporation -# Copyright 2014 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -from ironic.common.i18n import _ - -opts = [ - cfg.IntOpt('max_retry', - default=3, - help=_('Maximum retries for iBoot operations')), - cfg.IntOpt('retry_interval', - default=1, - help=_('Time (in seconds) between retry attempts for iBoot ' - 'operations')), - cfg.IntOpt('reboot_delay', - default=5, - min=0, - help=_('Time (in seconds) to sleep between when rebooting ' - '(powering off and on again).')) -] - -opt_group = cfg.OptGroup(name='iboot', - title='Options for the iBoot power driver') - - -def register_opts(conf): - conf.register_group(opt_group) - conf.register_opts(opts, group=opt_group) diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py index d71cfc531f..cd86a9cb69 100644 --- a/ironic/conf/opts.py +++ b/ironic/conf/opts.py @@ -31,7 +31,6 @@ _default_opt_lists = [ _opts = [ ('DEFAULT', itertools.chain(*_default_opt_lists)), ('agent', ironic.conf.agent.opts), - ('amt', ironic.conf.amt.opts), ('api', ironic.conf.api.opts), ('audit', ironic.conf.audit.opts), ('cimc', ironic.conf.cisco.cimc_opts), @@ -43,7 +42,6 @@ _opts = [ ('dhcp', ironic.conf.dhcp.opts), ('drac', ironic.conf.drac.opts), ('glance', ironic.conf.glance.list_opts()), - ('iboot', ironic.conf.iboot.opts), ('ilo', ironic.conf.ilo.opts), ('inspector', ironic.conf.inspector.list_opts()), ('ipmi', ironic.conf.ipmi.opts), diff --git a/ironic/drivers/agent.py b/ironic/drivers/agent.py index 92e56425c5..f1f9402236 100644 --- a/ironic/drivers/agent.py +++ b/ironic/drivers/agent.py @@ -18,11 +18,8 @@ from ironic.common import exception from ironic.common.i18n import _ from ironic.drivers import base from ironic.drivers.modules import agent -from ironic.drivers.modules.amt import management as amt_management -from ironic.drivers.modules.amt import power as amt_power from ironic.drivers.modules.cimc import management as cimc_mgmt from ironic.drivers.modules.cimc import power as cimc_power -from ironic.drivers.modules import iboot from ironic.drivers.modules import inspector from ironic.drivers.modules import ipminative from ironic.drivers.modules import ipmitool @@ -31,7 +28,6 @@ from ironic.drivers.modules import ssh from ironic.drivers.modules.ucs import management as ucs_mgmt from ironic.drivers.modules.ucs import power as ucs_power from ironic.drivers.modules import virtualbox -from ironic.drivers.modules import wol class AgentAndIPMIToolDriver(base.BaseDriver): @@ -155,30 +151,6 @@ class AgentAndVirtualBoxDriver(base.BaseDriver): self.raid = agent.AgentRAID() -class AgentAndAMTDriver(base.BaseDriver): - """Agent + AMT driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.amt.AMTPower` for power on/off and reboot with - :class:`ironic.drivers.modules.agent_deploy.AgentDeploy` for image - deployment. Implementations are in those respective classes; this - class is merely the glue between them. - """ - - supported = False - - def __init__(self): - if not importutils.try_import('pywsman'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import pywsman library")) - self.power = amt_power.AMTPower() - self.boot = pxe.PXEBoot() - self.deploy = agent.AgentDeploy() - self.management = amt_management.AMTManagement() - self.vendor = agent.AgentVendorInterface() - - class AgentAndUcsDriver(base.BaseDriver): """Agent + Cisco UCSM driver. @@ -225,46 +197,3 @@ class AgentAndCIMCDriver(base.BaseDriver): self.management = cimc_mgmt.CIMCManagement() self.inspect = inspector.Inspector.create_if_enabled( 'AgentAndCIMCDriver') - - -class AgentAndWakeOnLanDriver(base.BaseDriver): - """Agent + WakeOnLan driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.modules.wol.WakeOnLanPower` for power on with - :class:'ironic.driver.modules.agent.AgentDeploy' (for image deployment.) - Implementations are in those respective classes; - this class is merely the glue between them. - """ - - supported = False - - def __init__(self): - self.power = wol.WakeOnLanPower() - self.boot = pxe.PXEBoot() - self.deploy = agent.AgentDeploy() - self.vendor = agent.AgentVendorInterface() - - -class AgentAndIBootDriver(base.BaseDriver): - """Agent + IBoot PDU driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.modules.iboot.IBootPower` for power - on/off and reboot with - :class:'ironic.driver.modules.agent.AgentDeploy' (for image deployment.) - Implementations are in those respective classes; - this class is merely the glue between them. - """ - - supported = False - - def __init__(self): - if not importutils.try_import('iboot'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import iboot library")) - self.power = iboot.IBootPower() - self.boot = pxe.PXEBoot() - self.deploy = agent.AgentDeploy() - self.vendor = agent.AgentVendorInterface() diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py index a25e6c5c38..96864312f2 100644 --- a/ironic/drivers/fake.py +++ b/ironic/drivers/fake.py @@ -23,8 +23,6 @@ from ironic.common import exception from ironic.common.i18n import _ from ironic.drivers import base from ironic.drivers.modules import agent -from ironic.drivers.modules.amt import management as amt_mgmt -from ironic.drivers.modules.amt import power as amt_power from ironic.drivers.modules.cimc import management as cimc_mgmt from ironic.drivers.modules.cimc import power as cimc_power from ironic.drivers.modules.drac import deploy as drac_deploy @@ -34,7 +32,6 @@ from ironic.drivers.modules.drac import power as drac_power from ironic.drivers.modules.drac import raid as drac_raid from ironic.drivers.modules.drac import vendor_passthru as drac_vendor from ironic.drivers.modules import fake -from ironic.drivers.modules import iboot from ironic.drivers.modules.ilo import inspect as ilo_inspect from ironic.drivers.modules.ilo import management as ilo_management from ironic.drivers.modules.ilo import power as ilo_power @@ -57,7 +54,6 @@ from ironic.drivers.modules import ssh from ironic.drivers.modules.ucs import management as ucs_mgmt from ironic.drivers.modules.ucs import power as ucs_power from ironic.drivers.modules import virtualbox -from ironic.drivers.modules import wol from ironic.drivers import utils @@ -171,20 +167,6 @@ class FakeAgentDriver(base.BaseDriver): self.raid = agent.AgentRAID() -class FakeIBootDriver(base.BaseDriver): - """Fake iBoot driver.""" - - supported = False - - def __init__(self): - if not importutils.try_import('iboot'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import iboot library")) - self.power = iboot.IBootPower() - self.deploy = fake.FakeDeploy() - - class FakeIloDriver(base.BaseDriver): """Fake iLO driver, used in testing.""" @@ -272,21 +254,6 @@ class FakeIPMIToolInspectorDriver(base.BaseDriver): self.inspect = inspector.Inspector() -class FakeAMTDriver(base.BaseDriver): - """Fake AMT driver.""" - - supported = False - - def __init__(self): - if not importutils.try_import('pywsman'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import pywsman library")) - self.power = amt_power.AMTPower() - self.deploy = fake.FakeDeploy() - self.management = amt_mgmt.AMTManagement() - - class FakeMSFTOCSDriver(base.BaseDriver): """Fake MSFT OCS driver.""" @@ -324,16 +291,6 @@ class FakeCIMCDriver(base.BaseDriver): self.management = cimc_mgmt.CIMCManagement() -class FakeWakeOnLanDriver(base.BaseDriver): - """Fake Wake-On-Lan driver.""" - - supported = False - - def __init__(self): - self.power = wol.WakeOnLanPower() - self.deploy = fake.FakeDeploy() - - class FakeOneViewDriver(base.BaseDriver): """Fake OneView driver. For testing purposes. """ diff --git a/ironic/drivers/modules/amt/__init__.py b/ironic/drivers/modules/amt/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ironic/drivers/modules/amt/common.py b/ironic/drivers/modules/amt/common.py deleted file mode 100644 index ffb3e2b562..0000000000 --- a/ironic/drivers/modules/amt/common.py +++ /dev/null @@ -1,232 +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. -""" -Common functionalities for AMT Driver -""" -import time -from xml import etree - -from oslo_concurrency import processutils -from oslo_log import log as logging -from oslo_utils import importutils -import six - -from ironic.common import boot_devices -from ironic.common import exception -from ironic.common.i18n import _, _LE -from ironic.common import utils -from ironic.conf import CONF - - -pywsman = importutils.try_import('pywsman') - -_SOAP_ENVELOPE = 'http://www.w3.org/2003/05/soap-envelope' - -LOG = logging.getLogger(__name__) - -REQUIRED_PROPERTIES = { - 'amt_address': _('IP address or host name of the node. Required.'), - 'amt_password': _('Password. Required.'), - 'amt_username': _('Username to log into AMT system. Required.'), -} -OPTIONAL_PROPERTIES = { - 'amt_protocol': _('Protocol used for AMT endpoint. one of http, https; ' - 'default is "http". Optional.'), -} -COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() -COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) - -# TODO(lintan): More boot devices are supported by AMT, but not useful -# currently. Add them in the future. -BOOT_DEVICES_MAPPING = { - boot_devices.PXE: 'Intel(r) AMT: Force PXE Boot', - boot_devices.DISK: 'Intel(r) AMT: Force Hard-drive Boot', - boot_devices.CDROM: 'Intel(r) AMT: Force CD/DVD Boot', -} -DEFAULT_BOOT_DEVICE = boot_devices.DISK - -AMT_PROTOCOL_PORT_MAP = { - 'http': 16992, - 'https': 16993, -} - -# ReturnValue constants -RET_SUCCESS = '0' - -# A dict cache last awake call to AMT Interface -AMT_AWAKE_CACHE = {} - - -class Client(object): - """AMT client. - - Create a pywsman client to connect to the target server - """ - def __init__(self, address, protocol, username, password): - port = AMT_PROTOCOL_PORT_MAP[protocol] - path = '/wsman' - self.client = pywsman.Client(address, port, path, protocol, - username, password) - - def wsman_get(self, resource_uri, options=None): - """Get target server info - - :param options: client options - :param resource_uri: a URI to an XML schema - :returns: XmlDoc object - :raises: AMTFailure if get unexpected response. - :raises: AMTConnectFailure if unable to connect to the server. - """ - if options is None: - options = pywsman.ClientOptions() - doc = self.client.get(options, resource_uri) - item = 'Fault' - fault = xml_find(doc, _SOAP_ENVELOPE, item) - if fault is not None: - LOG.exception(_LE('Call to AMT with URI %(uri)s failed: ' - 'got Fault %(fault)s'), - {'uri': resource_uri, 'fault': fault.text}) - raise exception.AMTFailure(cmd='wsman_get') - return doc - - def wsman_invoke(self, options, resource_uri, method, data=None): - """Invoke method on target server - - :param options: client options - :param resource_uri: a URI to an XML schema - :param method: invoke method - :param data: a XmlDoc as invoke input - :returns: XmlDoc object - :raises: AMTFailure if get unexpected response. - :raises: AMTConnectFailure if unable to connect to the server. - """ - if data is None: - doc = self.client.invoke(options, resource_uri, method) - else: - doc = self.client.invoke(options, resource_uri, method, data) - item = "ReturnValue" - return_value = xml_find(doc, resource_uri, item).text - if return_value != RET_SUCCESS: - LOG.exception(_LE("Call to AMT with URI %(uri)s and " - "method %(method)s failed: return value " - "was %(value)s"), - {'uri': resource_uri, 'method': method, - 'value': return_value}) - raise exception.AMTFailure(cmd='wsman_invoke') - return doc - - -def parse_driver_info(node): - """Parses and creates AMT driver info - - :param node: an Ironic node object. - :returns: AMT driver info. - :raises: MissingParameterValue if any required parameters are missing. - :raises: InvalidParameterValue if any parameters have invalid values. - """ - - info = node.driver_info or {} - d_info = {} - missing_info = [] - - for param in REQUIRED_PROPERTIES: - value = info.get(param) - if value: - if not isinstance(value, six.binary_type): - value = value.encode() - d_info[param[4:]] = value - else: - missing_info.append(param) - - if missing_info: - raise exception.MissingParameterValue(_( - "AMT driver requires the following to be set in " - "node's driver_info: %s.") % missing_info) - - d_info['uuid'] = node.uuid - param = 'amt_protocol' - protocol = info.get(param, CONF.amt.get(param[4:])) - if protocol not in AMT_PROTOCOL_PORT_MAP: - raise exception.InvalidParameterValue( - _("Invalid protocol %s.") % protocol) - if not isinstance(value, six.binary_type): - protocol = protocol.encode() - d_info[param[4:]] = protocol - - return d_info - - -def get_wsman_client(node): - """Return a AMT Client object - - :param node: an Ironic node object. - :returns: a Client object - :raises: MissingParameterValue if any required parameters are missing. - :raises: InvalidParameterValue if any parameters have invalid values. - """ - driver_info = parse_driver_info(node) - client = Client(address=driver_info['address'], - protocol=driver_info['protocol'], - username=driver_info['username'], - password=driver_info['password']) - return client - - -def xml_find(doc, namespace, item): - """Find the first element with namespace and item, in the XML doc - - :param doc: a doc object. - :param namespace: the namespace of the element. - :param item: the element name. - :returns: the element object or None - :raises: AMTConnectFailure if unable to connect to the server. - """ - if doc is None: - raise exception.AMTConnectFailure() - tree = etree.ElementTree.fromstring(doc.root().string()) - query = ('.//{%(namespace)s}%(item)s' % {'namespace': namespace, - 'item': item}) - return tree.find(query) - - -def awake_amt_interface(node): - """Wake up AMT interface. - - AMT interface goes to sleep after a period of time if the host is off. - This method will ping AMT interface to wake it up. Because there is - no guarantee that the AMT address in driver_info is correct, only - ping the IP five times which is enough to wake it up. - - :param node: an Ironic node object. - :raises: AMTConnectFailure if unable to connect to the server. - """ - awake_interval = CONF.amt.awake_interval - if awake_interval == 0: - return - - now = time.time() - last_awake = AMT_AWAKE_CACHE.get(node.uuid, 0) - if now - last_awake > awake_interval: - cmd_args = ['ping', '-i', 0.2, '-c', 5, - node.driver_info['amt_address']] - try: - utils.execute(*cmd_args) - except processutils.ProcessExecutionError as err: - LOG.error(_LE('Unable to awake AMT interface on node ' - '%(node_id)s. Error: %(error)s'), - {'node_id': node.uuid, 'error': err}) - raise exception.AMTConnectFailure() - else: - LOG.debug(('Successfully awakened AMT interface on node ' - '%(node_id)s.'), {'node_id': node.uuid}) - AMT_AWAKE_CACHE[node.uuid] = now diff --git a/ironic/drivers/modules/amt/management.py b/ironic/drivers/modules/amt/management.py deleted file mode 100644 index d41d43cda6..0000000000 --- a/ironic/drivers/modules/amt/management.py +++ /dev/null @@ -1,247 +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. - -""" -AMT Management Driver -""" -import copy - -from oslo_log import log as logging -from oslo_utils import excutils -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _, _LE, _LI -from ironic.conductor import task_manager -from ironic.drivers import base -from ironic.drivers.modules.amt import common as amt_common -from ironic.drivers.modules.amt import resource_uris - -pywsman = importutils.try_import('pywsman') - -LOG = logging.getLogger(__name__) - - -_ADDRESS = 'http://schemas.xmlsoap.org/ws/2004/08/addressing' -_ANONYMOUS = 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous' -_WSMAN = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' - - -def _generate_change_boot_order_input(device): - """Generate Xmldoc as change_boot_order input. - - This generates a Xmldoc used as input for change_boot_order. - - :param device: the boot device. - :returns: Xmldoc. - """ - method_input = "ChangeBootOrder_INPUT" - namespace = resource_uris.CIM_BootConfigSetting - doc = pywsman.XmlDoc(method_input) - root = doc.root() - root.set_ns(namespace) - - child = root.add(namespace, 'Source', None) - child.add(_ADDRESS, 'Address', _ANONYMOUS) - - grand_child = child.add(_ADDRESS, 'ReferenceParameters', None) - grand_child.add(_WSMAN, 'ResourceURI', resource_uris.CIM_BootSourceSetting) - g_grand_child = grand_child.add(_WSMAN, 'SelectorSet', None) - g_g_grand_child = g_grand_child.add(_WSMAN, 'Selector', device) - g_g_grand_child.attr_add(_WSMAN, 'Name', 'InstanceID') - return doc - - -def _set_boot_device_order(node, boot_device): - """Set boot device order configuration of AMT Client. - - :param node: a node object - :param boot_device: the boot device - :raises: AMTFailure - :raises: AMTConnectFailure - """ - amt_common.awake_amt_interface(node) - client = amt_common.get_wsman_client(node) - device = amt_common.BOOT_DEVICES_MAPPING[boot_device] - doc = _generate_change_boot_order_input(device) - - method = 'ChangeBootOrder' - - options = pywsman.ClientOptions() - options.add_selector('InstanceID', 'Intel(r) AMT: Boot Configuration 0') - - try: - client.wsman_invoke(options, resource_uris.CIM_BootConfigSetting, - method, doc) - except (exception.AMTFailure, exception.AMTConnectFailure) as e: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Failed to set boot device %(boot_device)s for " - "node %(node_id)s with error: %(error)s."), - {'boot_device': boot_device, 'node_id': node.uuid, - 'error': e}) - else: - LOG.info(_LI("Successfully set boot device %(boot_device)s for " - "node %(node_id)s"), - {'boot_device': boot_device, 'node_id': node.uuid}) - - -def _generate_enable_boot_config_input(): - """Generate Xmldoc as enable_boot_config input. - - This generates a Xmldoc used as input for enable_boot_config. - - :returns: Xmldoc. - """ - method_input = "SetBootConfigRole_INPUT" - namespace = resource_uris.CIM_BootService - doc = pywsman.XmlDoc(method_input) - root = doc.root() - root.set_ns(namespace) - - child = root.add(namespace, 'BootConfigSetting', None) - child.add(_ADDRESS, 'Address', _ANONYMOUS) - - grand_child = child.add(_ADDRESS, 'ReferenceParameters', None) - grand_child.add(_WSMAN, 'ResourceURI', resource_uris.CIM_BootConfigSetting) - g_grand_child = grand_child.add(_WSMAN, 'SelectorSet', None) - g_g_grand_child = g_grand_child.add(_WSMAN, 'Selector', - 'Intel(r) AMT: Boot Configuration 0') - g_g_grand_child.attr_add(_WSMAN, 'Name', 'InstanceID') - root.add(namespace, 'Role', '1') - return doc - - -def _enable_boot_config(node): - """Enable boot configuration of AMT Client. - - :param node: a node object - :raises: AMTFailure - :raises: AMTConnectFailure - """ - amt_common.awake_amt_interface(node) - client = amt_common.get_wsman_client(node) - method = 'SetBootConfigRole' - doc = _generate_enable_boot_config_input() - options = pywsman.ClientOptions() - options.add_selector('Name', 'Intel(r) AMT Boot Service') - try: - client.wsman_invoke(options, resource_uris.CIM_BootService, - method, doc) - except (exception.AMTFailure, exception.AMTConnectFailure) as e: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Failed to enable boot config for node " - "%(node_id)s with error: %(error)s."), - {'node_id': node.uuid, 'error': e}) - else: - LOG.info(_LI("Successfully enabled boot config for node %(node_id)s."), - {'node_id': node.uuid}) - - -class AMTManagement(base.ManagementInterface): - - def get_properties(self): - return copy.deepcopy(amt_common.COMMON_PROPERTIES) - - def validate(self, task): - """Validate the driver_info in the node - - Check if the driver_info contains correct required fields - - :param task: a TaskManager instance contains the target node - :raises: MissingParameterValue if any required parameters are missing. - :raises: InvalidParameterValue if any parameters have invalid values. - """ - # FIXME(lintan): validate hangs if unable to reach AMT, so dont - # connect to the node until bug 1314961 is resolved. - amt_common.parse_driver_info(task.node) - - def get_supported_boot_devices(self, task): - """Get a list of the supported boot devices. - - :param task: a task from TaskManager. - :returns: A list with the supported boot devices. - """ - return list(amt_common.BOOT_DEVICES_MAPPING) - - @task_manager.require_exclusive_lock - def set_boot_device(self, task, device, persistent=False): - """Set the boot device for the task's node. - - Set the boot device to use on next boot of the node. - - :param task: a task from TaskManager. - :param device: the boot device - :param persistent: Boolean value. True if the boot device will - persist to all future boots, False if not. - Default: False. - :raises: InvalidParameterValue if an invalid boot device is specified. - """ - node = task.node - - if device not in amt_common.BOOT_DEVICES_MAPPING: - raise exception.InvalidParameterValue( - _("set_boot_device called with invalid device " - "%(device)s for node %(node_id)s." - ) % {'device': device, 'node_id': node.uuid}) - - # AMT/vPro doesn't support set boot_device persistent, so we have to - # save amt_boot_device/amt_boot_persistent in driver_internal_info. - driver_internal_info = node.driver_internal_info - driver_internal_info['amt_boot_device'] = device - driver_internal_info['amt_boot_persistent'] = persistent - node.driver_internal_info = driver_internal_info - node.save() - - def get_boot_device(self, task): - """Get the current boot device for the task's node. - - Returns the current boot device of the node. - - :param task: a task from TaskManager. - :returns: a dictionary containing: - - :boot_device: the boot device - :persistent: Whether the boot device will persist to all - future boots or not, None if it is unknown. - - """ - driver_internal_info = task.node.driver_internal_info - device = driver_internal_info.get('amt_boot_device') - persistent = driver_internal_info.get('amt_boot_persistent') - if not device: - device = amt_common.DEFAULT_BOOT_DEVICE - persistent = True - return {'boot_device': device, - 'persistent': persistent} - - def ensure_next_boot_device(self, node, boot_device): - """Set next boot device (one time only) of AMT Client. - - :param node: a node object - :param boot_device: the boot device - :raises: AMTFailure - :raises: AMTConnectFailure - """ - driver_internal_info = node.driver_internal_info - if not driver_internal_info.get('amt_boot_persistent'): - driver_internal_info['amt_boot_device'] = ( - amt_common.DEFAULT_BOOT_DEVICE) - driver_internal_info['amt_boot_persistent'] = True - node.driver_internal_info = driver_internal_info - node.save() - - _set_boot_device_order(node, boot_device) - _enable_boot_config(node) - - def get_sensors_data(self, task): - raise NotImplementedError() diff --git a/ironic/drivers/modules/amt/power.py b/ironic/drivers/modules/amt/power.py deleted file mode 100644 index 33d2d3a349..0000000000 --- a/ironic/drivers/modules/amt/power.py +++ /dev/null @@ -1,250 +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. - -""" -AMT Power Driver -""" -import copy - -from oslo_log import log as logging -from oslo_service import loopingcall -from oslo_utils import excutils -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _, _LE, _LI, _LW -from ironic.common import states -from ironic.conductor import task_manager -from ironic.conf import CONF -from ironic.drivers import base -from ironic.drivers.modules.amt import common as amt_common -from ironic.drivers.modules.amt import resource_uris - -pywsman = importutils.try_import('pywsman') - -LOG = logging.getLogger(__name__) - -AMT_POWER_MAP = { - states.POWER_ON: '2', - states.POWER_OFF: '8', -} - - -def _generate_power_action_input(action): - """Generate Xmldoc as set_power_state input. - - This generates a Xmldoc used as input for set_power_state. - - :param action: the power action. - :returns: Xmldoc. - """ - method_input = "RequestPowerStateChange_INPUT" - address = 'http://schemas.xmlsoap.org/ws/2004/08/addressing' - anonymous = ('http://schemas.xmlsoap.org/ws/2004/08/addressing/' - 'role/anonymous') - wsman = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' - namespace = resource_uris.CIM_PowerManagementService - - doc = pywsman.XmlDoc(method_input) - root = doc.root() - root.set_ns(namespace) - root.add(namespace, 'PowerState', action) - - child = root.add(namespace, 'ManagedElement', None) - child.add(address, 'Address', anonymous) - - grand_child = child.add(address, 'ReferenceParameters', None) - grand_child.add(wsman, 'ResourceURI', resource_uris.CIM_ComputerSystem) - - g_grand_child = grand_child.add(wsman, 'SelectorSet', None) - g_g_grand_child = g_grand_child.add(wsman, 'Selector', 'ManagedSystem') - g_g_grand_child.attr_add(wsman, 'Name', 'Name') - return doc - - -def _set_power_state(node, target_state): - """Set power state of the AMT Client. - - :param node: a node object. - :param target_state: desired power state. - :raises: AMTFailure - :raises: AMTConnectFailure - """ - amt_common.awake_amt_interface(node) - client = amt_common.get_wsman_client(node) - - method = 'RequestPowerStateChange' - options = pywsman.ClientOptions() - options.add_selector('Name', 'Intel(r) AMT Power Management Service') - - doc = _generate_power_action_input(AMT_POWER_MAP[target_state]) - try: - client.wsman_invoke(options, resource_uris.CIM_PowerManagementService, - method, doc) - except (exception.AMTFailure, exception.AMTConnectFailure) as e: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Failed to set power state %(state)s for " - "node %(node_id)s with error: %(error)s."), - {'state': target_state, 'node_id': node.uuid, - 'error': e}) - else: - LOG.info(_LI("Power state set to %(state)s for node %(node_id)s"), - {'state': target_state, 'node_id': node.uuid}) - - -def _power_status(node): - """Get the power status for a node. - - :param node: a node object. - :returns: one of ironic.common.states POWER_OFF, POWER_ON or ERROR. - :raises: AMTFailure. - :raises: AMTConnectFailure. - """ - amt_common.awake_amt_interface(node) - client = amt_common.get_wsman_client(node) - namespace = resource_uris.CIM_AssociatedPowerManagementService - try: - doc = client.wsman_get(namespace) - except (exception.AMTFailure, exception.AMTConnectFailure) as e: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Failed to get power state for node %(node_id)s " - "with error: %(error)s."), - {'node_id': node.uuid, 'error': e}) - - item = "PowerState" - power_state = amt_common.xml_find(doc, namespace, item).text - for state in AMT_POWER_MAP: - if power_state == AMT_POWER_MAP[state]: - return state - return states.ERROR - - -def _set_and_wait(task, target_state): - """Helper function for DynamicLoopingCall. - - This method changes the power state and polls AMT until the desired - power state is reached. - - :param task: a TaskManager instance contains the target node. - :param target_state: desired power state. - :returns: one of ironic.common.states. - :raises: PowerStateFailure if cannot set the node to target_state. - :raises: AMTFailure. - :raises: AMTConnectFailure - :raises: InvalidParameterValue - """ - node = task.node - driver = task.driver - if target_state not in (states.POWER_ON, states.POWER_OFF): - raise exception.InvalidParameterValue(_('Unsupported target_state: %s') - % target_state) - elif target_state == states.POWER_ON: - boot_device = node.driver_internal_info.get('amt_boot_device') - if boot_device and boot_device != amt_common.DEFAULT_BOOT_DEVICE: - driver.management.ensure_next_boot_device(node, boot_device) - - def _wait(status): - status['power'] = _power_status(node) - if status['power'] == target_state: - raise loopingcall.LoopingCallDone() - - if status['iter'] >= CONF.amt.max_attempts: - status['power'] = states.ERROR - LOG.warning(_LW("AMT failed to set power state %(state)s after " - "%(tries)s retries on node %(node_id)s."), - {'state': target_state, 'tries': status['iter'], - 'node_id': node.uuid}) - raise loopingcall.LoopingCallDone() - - try: - _set_power_state(node, target_state) - except Exception: - # Log failures but keep trying - LOG.warning(_LW("AMT set power state %(state)s for node %(node)s " - "- Attempt %(attempt)s times of %(max_attempt)s " - "failed."), - {'state': target_state, 'node': node.uuid, - 'attempt': status['iter'] + 1, - 'max_attempt': CONF.amt.max_attempts}) - status['iter'] += 1 - - status = {'power': None, 'iter': 0} - - timer = loopingcall.FixedIntervalLoopingCall(_wait, status) - timer.start(interval=CONF.amt.action_wait).wait() - - if status['power'] != target_state: - raise exception.PowerStateFailure(pstate=target_state) - - return status['power'] - - -class AMTPower(base.PowerInterface): - """AMT Power interface. - - This Power interface control the power of node by providing power on/off - and reset functions. - """ - - def get_properties(self): - return copy.deepcopy(amt_common.COMMON_PROPERTIES) - - def validate(self, task): - """Validate the driver_info in the node. - - Check if the driver_info contains correct required fields - - :param task: a TaskManager instance contains the target node. - :raises: MissingParameterValue if any required parameters are missing. - :raises: InvalidParameterValue if any parameters have invalid values. - """ - # FIXME(lintan): validate hangs if unable to reach AMT, so dont - # connect to the node until bug 1314961 is resolved. - amt_common.parse_driver_info(task.node) - - def get_power_state(self, task): - """Get the power state from the node. - - :param task: a TaskManager instance contains the target node. - :raises: AMTFailure. - :raises: AMTConnectFailure. - """ - return _power_status(task.node) - - @task_manager.require_exclusive_lock - def set_power_state(self, task, pstate): - """Set the power state of the node. - - Turn the node power on or off. - - :param task: a TaskManager instance contains the target node. - :param pstate: The desired power state of the node. - :raises: PowerStateFailure if the power cannot set to pstate. - :raises: AMTFailure. - :raises: AMTConnectFailure. - :raises: InvalidParameterValue - """ - _set_and_wait(task, pstate) - - @task_manager.require_exclusive_lock - def reboot(self, task): - """Cycle the power of the node - - :param task: a TaskManager instance contains the target node. - :raises: PowerStateFailure if failed to reboot. - :raises: AMTFailure. - :raises: AMTConnectFailure. - :raises: InvalidParameterValue - """ - _set_and_wait(task, states.POWER_OFF) - _set_and_wait(task, states.POWER_ON) diff --git a/ironic/drivers/modules/amt/resource_uris.py b/ironic/drivers/modules/amt/resource_uris.py deleted file mode 100644 index b2e12f5b8b..0000000000 --- a/ironic/drivers/modules/amt/resource_uris.py +++ /dev/null @@ -1,35 +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. - -""" -XML Schemas to define the requests sent to AMT -""" - -CIM_AssociatedPowerManagementService = ('http://schemas.dmtf.org/wbem/wscim/' - '1/cim-schema/2/' - 'CIM_AssociatedPowerManagementService') - -CIM_PowerManagementService = ('http://schemas.dmtf.org/wbem/wscim/1/' - 'cim-schema/2/CIM_PowerManagementService') - -CIM_ComputerSystem = ('http://schemas.dmtf.org/wbem/wscim/' - '1/cim-schema/2/CIM_ComputerSystem') - -CIM_BootConfigSetting = ('http://schemas.dmtf.org/wbem/wscim/' - '1/cim-schema/2/CIM_BootConfigSetting') - -CIM_BootSourceSetting = ('http://schemas.dmtf.org/wbem/wscim/' - '1/cim-schema/2/CIM_BootSourceSetting') - -CIM_BootService = ('http://schemas.dmtf.org/wbem/wscim/' - '1/cim-schema/2/CIM_BootService') diff --git a/ironic/drivers/modules/amt/vendor.py b/ironic/drivers/modules/amt/vendor.py deleted file mode 100644 index 365f5e3636..0000000000 --- a/ironic/drivers/modules/amt/vendor.py +++ /dev/null @@ -1,30 +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. -""" -AMT Vendor Methods -""" - -from ironic.common import boot_devices -from ironic.conductor import task_manager -from ironic.drivers.modules import deploy_utils -from ironic.drivers.modules import iscsi_deploy - - -class AMTPXEVendorPassthru(iscsi_deploy.VendorPassthru): - - @task_manager.require_exclusive_lock - def continue_deploy(self, task): - if deploy_utils.get_boot_option(task.node) == "netboot": - task.driver.management.ensure_next_boot_device(task.node, - boot_devices.PXE) - super(AMTPXEVendorPassthru, self).continue_deploy(task) diff --git a/ironic/drivers/modules/iboot.py b/ironic/drivers/modules/iboot.py deleted file mode 100644 index d01faf590e..0000000000 --- a/ironic/drivers/modules/iboot.py +++ /dev/null @@ -1,257 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2014 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Ironic iBoot PDU power manager. -""" - -import time - -from oslo_log import log as logging -from oslo_service import loopingcall -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _, _LW -from ironic.common import states -from ironic.common import utils -from ironic.conductor import task_manager -from ironic.conf import CONF -from ironic.drivers import base - -iboot = importutils.try_import('iboot') - -LOG = logging.getLogger(__name__) - -REQUIRED_PROPERTIES = { - 'iboot_address': _("IP address of the node. Required."), - 'iboot_username': _("username. Required."), - 'iboot_password': _("password. Required."), -} -OPTIONAL_PROPERTIES = { - 'iboot_relay_id': _("iBoot PDU relay id; default is 1. Optional."), - 'iboot_port': _("iBoot PDU port; default is 9100. Optional."), -} -COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() -COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) - - -def _parse_driver_info(node): - info = node.driver_info or {} - missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)] - if missing_info: - raise exception.MissingParameterValue( - _("Missing the following iBoot credentials in node's" - " driver_info: %s.") % missing_info) - - address = info.get('iboot_address', None) - username = info.get('iboot_username', None) - password = info.get('iboot_password', None) - - relay_id = info.get('iboot_relay_id', 1) - try: - relay_id = int(relay_id) - except ValueError: - raise exception.InvalidParameterValue( - _("iBoot PDU relay id must be an integer.")) - - port = info.get('iboot_port', 9100) - port = utils.validate_network_port(port, 'iboot_port') - - return { - 'address': address, - 'username': username, - 'password': password, - 'port': port, - 'relay_id': relay_id, - 'uuid': node.uuid, - } - - -def _get_connection(driver_info): - # NOTE: python-iboot wants username and password as strings (not unicode) - return iboot.iBootInterface(driver_info['address'], - str(driver_info['username']), - str(driver_info['password']), - port=driver_info['port'], - num_relays=driver_info['relay_id']) - - -def _switch(driver_info, enabled): - conn = _get_connection(driver_info) - relay_id = driver_info['relay_id'] - - def _wait_for_switch(mutable): - if mutable['retries'] > CONF.iboot.max_retry: - LOG.warning(_LW( - 'Reached maximum number of attempts (%(attempts)d) to set ' - 'power state for node %(node)s to "%(op)s"'), - {'attempts': mutable['retries'], 'node': driver_info['uuid'], - 'op': states.POWER_ON if enabled else states.POWER_OFF}) - raise loopingcall.LoopingCallDone() - - try: - mutable['retries'] += 1 - mutable['response'] = conn.switch(relay_id, enabled) - if mutable['response']: - raise loopingcall.LoopingCallDone() - except (TypeError, IndexError): - LOG.warning(_LW("Cannot call set power state for node '%(node)s' " - "at relay '%(relay)s'. iBoot switch() failed."), - {'node': driver_info['uuid'], 'relay': relay_id}) - - mutable = {'response': False, 'retries': 0} - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_switch, - mutable) - timer.start(interval=CONF.iboot.retry_interval).wait() - return mutable['response'] - - -def _sleep_switch(seconds): - """Function broken out for testing purpose.""" - time.sleep(seconds) - - -def _check_power_state(driver_info, pstate): - """Function to check power state is correct. Up to max retries.""" - # always try once + number of retries - for num in range(0, 1 + CONF.iboot.max_retry): - state = _power_status(driver_info) - if state == pstate: - return - if num < CONF.iboot.max_retry: - time.sleep(CONF.iboot.retry_interval) - raise exception.PowerStateFailure(pstate=pstate) - - -def _power_status(driver_info): - conn = _get_connection(driver_info) - relay_id = driver_info['relay_id'] - - def _wait_for_power_status(mutable): - - if mutable['retries'] > CONF.iboot.max_retry: - LOG.warning(_LW( - 'Reached maximum number of attempts (%(attempts)d) to get ' - 'power state for node %(node)s'), - {'attempts': mutable['retries'], 'node': driver_info['uuid']}) - raise loopingcall.LoopingCallDone() - - try: - mutable['retries'] += 1 - response = conn.get_relays() - status = response[relay_id - 1] - if status: - mutable['state'] = states.POWER_ON - else: - mutable['state'] = states.POWER_OFF - raise loopingcall.LoopingCallDone() - except (TypeError, IndexError): - LOG.warning(_LW("Cannot get power state for node '%(node)s' at " - "relay '%(relay)s'. iBoot get_relays() failed."), - {'node': driver_info['uuid'], 'relay': relay_id}) - - mutable = {'state': states.ERROR, 'retries': 0} - - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_status, - mutable) - timer.start(interval=CONF.iboot.retry_interval).wait() - return mutable['state'] - - -class IBootPower(base.PowerInterface): - """iBoot PDU Power Driver for Ironic - - This PowerManager class provides a mechanism for controlling power state - via an iBoot capable device. - - Requires installation of python-iboot: - - https://github.com/darkip/python-iboot - - """ - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task): - """Validate driver_info for iboot driver. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if iboot parameters are invalid. - :raises: MissingParameterValue if required iboot parameters are - missing. - - """ - _parse_driver_info(task.node) - - def get_power_state(self, task): - """Get the current power state of the task's node. - - :param task: a TaskManager instance containing the node to act on. - :returns: one of ironic.common.states POWER_OFF, POWER_ON or ERROR. - :raises: InvalidParameterValue if iboot parameters are invalid. - :raises: MissingParameterValue if required iboot parameters are - missing. - - """ - driver_info = _parse_driver_info(task.node) - return _power_status(driver_info) - - @task_manager.require_exclusive_lock - def set_power_state(self, task, pstate): - """Turn the power on or off. - - :param task: a TaskManager instance containing the node to act on. - :param pstate: The desired power state, one of ironic.common.states - POWER_ON, POWER_OFF. - :raises: InvalidParameterValue if iboot parameters are invalid or if - an invalid power state was specified. - :raises: MissingParameterValue if required iboot parameters are - missing. - :raises: PowerStateFailure if the power couldn't be set to pstate. - - """ - driver_info = _parse_driver_info(task.node) - if pstate == states.POWER_ON: - _switch(driver_info, True) - elif pstate == states.POWER_OFF: - _switch(driver_info, False) - else: - raise exception.InvalidParameterValue( - _("set_power_state called with invalid " - "power state %s.") % pstate) - - _check_power_state(driver_info, pstate) - - @task_manager.require_exclusive_lock - def reboot(self, task): - """Cycles the power to the task's node. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if iboot parameters are invalid. - :raises: MissingParameterValue if required iboot parameters are - missing. - :raises: PowerStateFailure if the final state of the node is not - POWER_ON. - - """ - driver_info = _parse_driver_info(task.node) - _switch(driver_info, False) - _sleep_switch(CONF.iboot.reboot_delay) - _switch(driver_info, True) - _check_power_state(driver_info, states.POWER_ON) diff --git a/ironic/drivers/modules/wol.py b/ironic/drivers/modules/wol.py deleted file mode 100644 index 9c6d232029..0000000000 --- a/ironic/drivers/modules/wol.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright 2015 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Ironic Wake-On-Lan power manager. -""" - -import contextlib -import socket -import time - -from oslo_log import log - -from ironic.common import exception -from ironic.common.i18n import _, _LI -from ironic.common import states -from ironic.common import utils -from ironic.conductor import task_manager -from ironic.drivers import base - -LOG = log.getLogger(__name__) - -REQUIRED_PROPERTIES = {} -OPTIONAL_PROPERTIES = { - 'wol_host': _('Broadcast IP address; defaults to ' - '255.255.255.255. Optional.'), - 'wol_port': _("Destination port; defaults to 9. Optional."), -} -COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() -COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) - - -def _send_magic_packets(task, dest_host, dest_port): - """Create and send magic packets. - - Creates and sends a magic packet for each MAC address registered in - the Node. - - :param task: a TaskManager instance containing the node to act on. - :param dest_host: The broadcast to this IP address. - :param dest_port: The destination port. - :raises: WolOperationError if an error occur when connecting to the - host or sending the magic packets - - """ - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - with contextlib.closing(s) as sock: - for port in task.ports: - address = port.address.replace(':', '') - - # TODO(lucasagomes): Implement sending the magic packets with - # SecureON password feature. If your NIC is capable of, you can - # set the password of your SecureON using the ethtool utility. - data = 'FFFFFFFFFFFF' + (address * 16) - packet = bytearray.fromhex(data) - - try: - sock.sendto(packet, (dest_host, dest_port)) - except socket.error as e: - msg = (_("Failed to send Wake-On-Lan magic packets to " - "node %(node)s port %(port)s. Error: %(error)s") % - {'node': task.node.uuid, 'port': port.address, - 'error': e}) - LOG.exception(msg) - raise exception.WolOperationError(msg) - - # let's not flood the network with broadcast packets - time.sleep(0.5) - - -def _parse_parameters(task): - driver_info = task.node.driver_info - host = driver_info.get('wol_host', '255.255.255.255') - port = driver_info.get('wol_port', 9) - port = utils.validate_network_port(port, 'wol_port') - - if len(task.ports) < 1: - raise exception.MissingParameterValue(_( - 'Wake-On-Lan needs at least one port resource to be ' - 'registered in the node')) - - return {'host': host, 'port': port} - - -class WakeOnLanPower(base.PowerInterface): - """Wake-On-Lan Driver for Ironic - - This PowerManager class provides a mechanism for controlling power - state via Wake-On-Lan. - - """ - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task): - """Validate driver. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if parameters are invalid. - :raises: MissingParameterValue if required parameters are missing. - - """ - _parse_parameters(task) - - def get_power_state(self, task): - """Not supported. Get the current power state of the task's node. - - This operation is not supported by the Wake-On-Lan driver. So - value returned will be from the database and may not reflect - the actual state of the system. - - :returns: POWER_OFF if power state is not set otherwise return - the node's power_state value from the database. - - """ - pstate = task.node.power_state - return states.POWER_OFF if pstate is states.NOSTATE else pstate - - @task_manager.require_exclusive_lock - def set_power_state(self, task, pstate): - """Wakes the task's node on power on. Powering off is not supported. - - Wakes the task's node on. Wake-On-Lan does not support powering - the task's node off so, just log it. - - :param task: a TaskManager instance containing the node to act on. - :param pstate: The desired power state, one of ironic.common.states - POWER_ON, POWER_OFF. - :raises: InvalidParameterValue if parameters are invalid. - :raises: MissingParameterValue if required parameters are missing. - :raises: WolOperationError if an error occur when sending the - magic packets - - """ - node = task.node - params = _parse_parameters(task) - if pstate == states.POWER_ON: - _send_magic_packets(task, params['host'], params['port']) - elif pstate == states.POWER_OFF: - LOG.info(_LI('Power off called for node %s. Wake-On-Lan does not ' - 'support this operation. Manual intervention ' - 'required to perform this action.'), node.uuid) - else: - raise exception.InvalidParameterValue(_( - "set_power_state called for Node %(node)s with invalid " - "power state %(pstate)s.") % {'node': node.uuid, - 'pstate': pstate}) - - @task_manager.require_exclusive_lock - def reboot(self, task): - """Not supported. Cycles the power to the task's node. - - This operation is not fully supported by the Wake-On-Lan - driver. So this method will just try to power the task's node on. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if parameters are invalid. - :raises: MissingParameterValue if required parameters are missing. - :raises: WolOperationError if an error occur when sending the - magic packets - - """ - LOG.info(_LI('Reboot called for node %s. Wake-On-Lan does ' - 'not fully support this operation. Trying to ' - 'power on the node.'), task.node.uuid) - self.set_power_state(task, states.POWER_ON) diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py index db855e3ed6..440c9bd5be 100644 --- a/ironic/drivers/pxe.py +++ b/ironic/drivers/pxe.py @@ -23,12 +23,8 @@ from ironic.common import exception from ironic.common.i18n import _ from ironic.drivers import base from ironic.drivers.modules import agent -from ironic.drivers.modules.amt import management as amt_management -from ironic.drivers.modules.amt import power as amt_power -from ironic.drivers.modules.amt import vendor as amt_vendor from ironic.drivers.modules.cimc import management as cimc_mgmt from ironic.drivers.modules.cimc import power as cimc_power -from ironic.drivers.modules import iboot from ironic.drivers.modules.ilo import console as ilo_console from ironic.drivers.modules.ilo import deploy as ilo_deploy from ironic.drivers.modules.ilo import inspect as ilo_inspect @@ -51,7 +47,6 @@ from ironic.drivers.modules import ssh from ironic.drivers.modules.ucs import management as ucs_mgmt from ironic.drivers.modules.ucs import power as ucs_power from ironic.drivers.modules import virtualbox -from ironic.drivers.modules import wol class PXEAndIPMIToolDriver(base.BaseDriver): @@ -176,30 +171,6 @@ class PXEAndSeaMicroDriver(base.BaseDriver): self.console = seamicro.ShellinaboxConsole() -class PXEAndIBootDriver(base.BaseDriver): - """PXE + IBoot PDU driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.modules.iboot.IBootPower` for power - on/off and reboot with - :class:`ironic.drivers.modules.iscsi_deploy.ISCSIDeploy` for - image deployment. Implementations are in those respective classes; - this class is merely the glue between them. - """ - - supported = False - - def __init__(self): - if not importutils.try_import('iboot'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import iboot library")) - self.power = iboot.IBootPower() - self.boot = pxe.PXEBoot() - self.deploy = iscsi_deploy.ISCSIDeploy() - self.vendor = iscsi_deploy.VendorPassthru() - - class PXEAndIloDriver(base.BaseDriver): """PXE + Ilo Driver using IloClient interface. @@ -297,30 +268,6 @@ class PXEAndVirtualBoxDriver(base.BaseDriver): self.raid = agent.AgentRAID() -class PXEAndAMTDriver(base.BaseDriver): - """PXE + AMT driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.amt.AMTPower` for power on/off and reboot with - :class:`ironic.drivers.modules.iscsi_deploy.ISCSIDeploy` for image - deployment. Implementations are in those respective classes; this - class is merely the glue between them. - """ - - supported = False - - def __init__(self): - if not importutils.try_import('pywsman'): - raise exception.DriverLoadError( - driver=self.__class__.__name__, - reason=_("Unable to import pywsman library")) - self.power = amt_power.AMTPower() - self.boot = pxe.PXEBoot() - self.deploy = iscsi_deploy.ISCSIDeploy() - self.management = amt_management.AMTManagement() - self.vendor = amt_vendor.AMTPXEVendorPassthru() - - class PXEAndMSFTOCSDriver(base.BaseDriver): """PXE + MSFT OCS driver. @@ -384,22 +331,3 @@ class PXEAndCIMCDriver(base.BaseDriver): self.management = cimc_mgmt.CIMCManagement() self.inspect = inspector.Inspector.create_if_enabled( 'PXEAndCIMCDriver') - - -class PXEAndWakeOnLanDriver(base.BaseDriver): - """PXE + WakeOnLan driver. - - This driver implements the `core` functionality, combining - :class:`ironic.drivers.modules.wol.WakeOnLanPower` for power on - :class:`ironic.drivers.modules.iscsi_deploy.ISCSIDeploy` for image - deployment. Implementations are in those respective classes; - this class is merely the glue between them. - """ - - supported = False - - def __init__(self): - self.power = wol.WakeOnLanPower() - self.boot = pxe.PXEBoot() - self.deploy = iscsi_deploy.ISCSIDeploy() - self.vendor = iscsi_deploy.VendorPassthru() diff --git a/ironic/tests/unit/db/utils.py b/ironic/tests/unit/db/utils.py index 5bdfb44aae..1000a4f8b2 100644 --- a/ironic/tests/unit/db/utils.py +++ b/ironic/tests/unit/db/utils.py @@ -127,15 +127,6 @@ def get_test_irmc_info(): } -def get_test_amt_info(): - return { - "amt_address": "1.2.3.4", - "amt_protocol": "http", - "amt_username": "admin", - "amt_password": "fake", - } - - def get_test_msftocs_info(): return { "msftocs_base_url": "http://fakehost:8000", @@ -170,14 +161,6 @@ def get_test_agent_driver_internal_info(): } -def get_test_iboot_info(): - return { - "iboot_address": "1.2.3.4", - "iboot_username": "admin", - "iboot_password": "fake", - } - - def get_test_snmp_info(**kw): result = { "snmp_driver": kw.get("snmp_driver", "teltronix"), diff --git a/ironic/tests/unit/drivers/modules/amt/__init__.py b/ironic/tests/unit/drivers/modules/amt/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ironic/tests/unit/drivers/modules/amt/test_common.py b/ironic/tests/unit/drivers/modules/amt/test_common.py deleted file mode 100644 index 68e8ea48d2..0000000000 --- a/ironic/tests/unit/drivers/modules/amt/test_common.py +++ /dev/null @@ -1,217 +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. - -""" -Test class for AMT Common -""" - -import mock -from oslo_concurrency import processutils -from oslo_config import cfg -import time - -from ironic.common import exception -from ironic.common import utils -from ironic.drivers.modules.amt import common as amt_common -from ironic.drivers.modules.amt import resource_uris -from ironic.tests import base -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.drivers.modules.amt import utils as test_utils -from ironic.tests.unit.drivers import third_party_driver_mock_specs \ - as mock_specs -from ironic.tests.unit.objects import utils as obj_utils - -INFO_DICT = db_utils.get_test_amt_info() -CONF = cfg.CONF - - -class AMTCommonMethodsTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTCommonMethodsTestCase, self).setUp() - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=INFO_DICT) - - def test_parse_driver_info(self): - info = amt_common.parse_driver_info(self.node) - - self.assertEqual(b'1.2.3.4', info['address']) - self.assertEqual(b'admin', info['username']) - self.assertEqual(b'fake', info['password']) - self.assertEqual(INFO_DICT['amt_protocol'], info['protocol']) - self.assertEqual('1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - info['uuid']) - - def test_parse_driver_info_missing_address(self): - del self.node.driver_info['amt_address'] - - self.assertRaises(exception.MissingParameterValue, - amt_common.parse_driver_info, self.node) - - def test_parse_driver_info_missing_username(self): - del self.node.driver_info['amt_username'] - - self.assertRaises(exception.MissingParameterValue, - amt_common.parse_driver_info, self.node) - - def test_parse_driver_info_missing_password(self): - del self.node.driver_info['amt_password'] - self.assertRaises(exception.MissingParameterValue, - amt_common.parse_driver_info, self.node) - - def test_parse_driver_info_missing_protocol(self): - del self.node.driver_info['amt_protocol'] - info = amt_common.parse_driver_info(self.node) - self.assertEqual('http', info['protocol']) - - def test_parse_driver_info_wrong_protocol(self): - self.node.driver_info['amt_protocol'] = 'fake-protocol' - self.assertRaises(exception.InvalidParameterValue, - amt_common.parse_driver_info, self.node) - - @mock.patch.object(amt_common, 'Client', spec_set=True, autospec=True) - def test_get_wsman_client(self, mock_client): - info = amt_common.parse_driver_info(self.node) - amt_common.get_wsman_client(self.node) - options = {'address': info['address'], - 'protocol': info['protocol'], - 'username': info['username'], - 'password': info['password']} - - mock_client.assert_called_once_with(**options) - - def test_xml_find(self): - namespace = 'http://fake' - value = 'fake_value' - test_xml = test_utils.build_soap_xml([{'test_element': value}], - namespace) - mock_doc = test_utils.mock_wsman_root(test_xml) - - result = amt_common.xml_find(mock_doc, namespace, 'test_element') - self.assertEqual(value, result.text) - - def test_xml_find_fail(self): - mock_doc = None - self.assertRaises(exception.AMTConnectFailure, - amt_common.xml_find, - mock_doc, 'namespace', 'test_element') - - -@mock.patch.object(amt_common, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC) -class AMTCommonClientTestCase(base.TestCase): - def setUp(self): - super(AMTCommonClientTestCase, self).setUp() - self.info = {key[4:]: INFO_DICT[key] for key in INFO_DICT} - - def test_wsman_get(self, mock_client_pywsman): - namespace = resource_uris.CIM_AssociatedPowerManagementService - result_xml = test_utils.build_soap_xml([{'PowerState': - '2'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.get.return_value = mock_doc - client = amt_common.Client(**self.info) - - client.wsman_get(namespace) - mock_pywsman.get.assert_called_once_with(mock.ANY, namespace) - - def test_wsman_get_fail(self, mock_client_pywsman): - namespace = amt_common._SOAP_ENVELOPE - result_xml = test_utils.build_soap_xml([{'Fault': 'fault'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.get.return_value = mock_doc - client = amt_common.Client(**self.info) - - self.assertRaises(exception.AMTFailure, client.wsman_get, namespace) - mock_pywsman.get.assert_called_once_with(mock.ANY, namespace) - - def test_wsman_invoke(self, mock_client_pywsman): - namespace = resource_uris.CIM_BootSourceSetting - result_xml = test_utils.build_soap_xml([{'ReturnValue': - '0'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_doc - method = 'ChangeBootOrder' - options = mock.Mock(spec_set=[]) - client = amt_common.Client(**self.info) - doc = None - client.wsman_invoke(options, namespace, method, doc) - mock_pywsman.invoke.assert_called_once_with(options, namespace, method) - doc = 'fake-input' - client.wsman_invoke(options, namespace, method, doc) - mock_pywsman.invoke.assert_called_with(options, namespace, method, doc) - - def test_wsman_invoke_fail(self, mock_client_pywsman): - namespace = resource_uris.CIM_BootSourceSetting - result_xml = test_utils.build_soap_xml([{'ReturnValue': - '2'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_doc - method = 'fake-method' - options = mock.Mock(spec_set=[]) - - client = amt_common.Client(**self.info) - - self.assertRaises(exception.AMTFailure, - client.wsman_invoke, - options, namespace, method) - mock_pywsman.invoke.assert_called_once_with(options, namespace, method) - - -class AwakeAMTInterfaceTestCase(db_base.DbTestCase): - def setUp(self): - super(AwakeAMTInterfaceTestCase, self).setUp() - amt_common.AMT_AWAKE_CACHE = {} - self.info = INFO_DICT - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=self.info) - - @mock.patch.object(utils, 'execute', spec_set=True, autospec=True) - def test_awake_amt_interface(self, mock_ex): - amt_common.awake_amt_interface(self.node) - expected_args = ['ping', '-i', 0.2, '-c', 5, '1.2.3.4'] - mock_ex.assert_called_once_with(*expected_args) - - @mock.patch.object(utils, 'execute', spec_set=True, autospec=True) - def test_awake_amt_interface_fail(self, mock_ex): - mock_ex.side_effect = processutils.ProcessExecutionError('x') - self.assertRaises(exception.AMTConnectFailure, - amt_common.awake_amt_interface, - self.node) - - @mock.patch.object(utils, 'execute', spec_set=True, autospec=True) - def test_awake_amt_interface_in_cache_time(self, mock_ex): - amt_common.AMT_AWAKE_CACHE[self.node.uuid] = time.time() - amt_common.awake_amt_interface(self.node) - self.assertFalse(mock_ex.called) - - @mock.patch.object(utils, 'execute', spec_set=True, autospec=True) - def test_awake_amt_interface_disable(self, mock_ex): - CONF.set_override('awake_interval', 0, 'amt') - amt_common.awake_amt_interface(self.node) - self.assertFalse(mock_ex.called) - - def test_out_range_protocol(self): - self.assertRaises(ValueError, cfg.CONF.set_override, - 'protocol', 'fake', 'amt', - enforce_type=True) diff --git a/ironic/tests/unit/drivers/modules/amt/test_management.py b/ironic/tests/unit/drivers/modules/amt/test_management.py deleted file mode 100644 index 18703f48a1..0000000000 --- a/ironic/tests/unit/drivers/modules/amt/test_management.py +++ /dev/null @@ -1,242 +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. - -""" -Test class for AMT ManagementInterface -""" - -import mock - -from ironic.common import boot_devices -from ironic.common import exception -from ironic.conductor import task_manager -from ironic.drivers.modules.amt import common as amt_common -from ironic.drivers.modules.amt import management as amt_mgmt -from ironic.drivers.modules.amt import resource_uris -from ironic.tests.unit.conductor import mgr_utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.drivers.modules.amt import utils as test_utils -from ironic.tests.unit.drivers import third_party_driver_mock_specs \ - as mock_specs -from ironic.tests.unit.objects import utils as obj_utils - -INFO_DICT = db_utils.get_test_amt_info() - - -@mock.patch.object(amt_common, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC) -class AMTManagementInteralMethodsTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTManagementInteralMethodsTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_amt') - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=INFO_DICT) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - def test__set_boot_device_order(self, mock_aw, mock_client_pywsman): - namespace = resource_uris.CIM_BootConfigSetting - device = boot_devices.PXE - result_xml = test_utils.build_soap_xml([{'ReturnValue': '0'}], - namespace) - mock_xml = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_xml - - amt_mgmt._set_boot_device_order(self.node, device) - - mock_pywsman.invoke.assert_called_once_with( - mock.ANY, namespace, 'ChangeBootOrder', mock.ANY) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - def test__set_boot_device_order_fail(self, mock_aw, mock_client_pywsman): - namespace = resource_uris.CIM_BootConfigSetting - device = boot_devices.PXE - result_xml = test_utils.build_soap_xml([{'ReturnValue': '2'}], - namespace) - mock_xml = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_xml - - self.assertRaises(exception.AMTFailure, - amt_mgmt._set_boot_device_order, self.node, device) - mock_pywsman.invoke.assert_called_once_with( - mock.ANY, namespace, 'ChangeBootOrder', mock.ANY) - - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = None - - self.assertRaises(exception.AMTConnectFailure, - amt_mgmt._set_boot_device_order, self.node, device) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - def test__enable_boot_config(self, mock_aw, mock_client_pywsman): - namespace = resource_uris.CIM_BootService - result_xml = test_utils.build_soap_xml([{'ReturnValue': '0'}], - namespace) - mock_xml = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_xml - - amt_mgmt._enable_boot_config(self.node) - - mock_pywsman.invoke.assert_called_once_with( - mock.ANY, namespace, 'SetBootConfigRole', mock.ANY) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - def test__enable_boot_config_fail(self, mock_aw, mock_client_pywsman): - namespace = resource_uris.CIM_BootService - result_xml = test_utils.build_soap_xml([{'ReturnValue': '2'}], - namespace) - mock_xml = test_utils.mock_wsman_root(result_xml) - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = mock_xml - - self.assertRaises(exception.AMTFailure, - amt_mgmt._enable_boot_config, self.node) - mock_pywsman.invoke.assert_called_once_with( - mock.ANY, namespace, 'SetBootConfigRole', mock.ANY) - - mock_pywsman = mock_client_pywsman.Client.return_value - mock_pywsman.invoke.return_value = None - - self.assertRaises(exception.AMTConnectFailure, - amt_mgmt._enable_boot_config, self.node) - self.assertTrue(mock_aw.called) - - -class AMTManagementTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTManagementTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_amt') - self.info = INFO_DICT - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=self.info) - - def test_get_properties(self): - expected = amt_common.COMMON_PROPERTIES - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(expected, task.driver.get_properties()) - - @mock.patch.object(amt_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test_validate(self, mock_drvinfo): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.management.validate(task) - mock_drvinfo.assert_called_once_with(task.node) - - @mock.patch.object(amt_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test_validate_fail(self, mock_drvinfo): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - mock_drvinfo.side_effect = exception.InvalidParameterValue('x') - self.assertRaises(exception.InvalidParameterValue, - task.driver.management.validate, - task) - - def test_get_supported_boot_devices(self): - expected = [boot_devices.PXE, boot_devices.DISK, boot_devices.CDROM] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual( - sorted(expected), - sorted(task.driver.management. - get_supported_boot_devices(task))) - - def test_set_boot_device_one_time(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.management.set_boot_device(task, 'pxe') - self.assertEqual('pxe', - task.node.driver_internal_info["amt_boot_device"]) - self.assertFalse( - task.node.driver_internal_info["amt_boot_persistent"]) - - def test_set_boot_device_persistent(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.management.set_boot_device(task, 'pxe', - persistent=True) - self.assertEqual('pxe', - task.node.driver_internal_info["amt_boot_device"]) - self.assertTrue( - task.node.driver_internal_info["amt_boot_persistent"]) - - def test_set_boot_device_fail(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaises(exception.InvalidParameterValue, - task.driver.management.set_boot_device, - task, 'fake-device') - - @mock.patch.object(amt_mgmt, '_enable_boot_config', spec_set=True, - autospec=True) - @mock.patch.object(amt_mgmt, '_set_boot_device_order', spec_set=True, - autospec=True) - def test_ensure_next_boot_device_one_time(self, mock_sbdo, mock_ebc): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - device = boot_devices.PXE - task.node.driver_internal_info['amt_boot_device'] = 'pxe' - task.driver.management.ensure_next_boot_device(task.node, device) - self.assertEqual('disk', - task.node.driver_internal_info["amt_boot_device"]) - self.assertTrue( - task.node.driver_internal_info["amt_boot_persistent"]) - mock_sbdo.assert_called_once_with(task.node, device) - mock_ebc.assert_called_once_with(task.node) - - @mock.patch.object(amt_mgmt, '_enable_boot_config', spec_set=True, - autospec=True) - @mock.patch.object(amt_mgmt, '_set_boot_device_order', spec_set=True, - autospec=True) - def test_ensure_next_boot_device_persistent(self, mock_sbdo, mock_ebc): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - device = boot_devices.PXE - task.node.driver_internal_info['amt_boot_device'] = 'pxe' - task.node.driver_internal_info['amt_boot_persistent'] = True - task.driver.management.ensure_next_boot_device(task.node, device) - self.assertEqual('pxe', - task.node.driver_internal_info["amt_boot_device"]) - self.assertTrue( - task.node.driver_internal_info["amt_boot_persistent"]) - mock_sbdo.assert_called_once_with(task.node, device) - mock_ebc.assert_called_once_with(task.node) - - def test_get_boot_device(self): - expected = {'boot_device': boot_devices.DISK, 'persistent': True} - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(expected, - task.driver.management.get_boot_device(task)) - - def test_get_sensor_data(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertRaises(NotImplementedError, - task.driver.management.get_sensors_data, - task) diff --git a/ironic/tests/unit/drivers/modules/amt/test_power.py b/ironic/tests/unit/drivers/modules/amt/test_power.py deleted file mode 100644 index d2e9d4bbac..0000000000 --- a/ironic/tests/unit/drivers/modules/amt/test_power.py +++ /dev/null @@ -1,293 +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. - -""" -Test class for AMT ManagementInterface -""" - -import mock -from oslo_config import cfg - -from ironic.common import boot_devices -from ironic.common import exception -from ironic.common import states -from ironic.conductor import task_manager -from ironic.drivers.modules.amt import common as amt_common -from ironic.drivers.modules.amt import management as amt_mgmt -from ironic.drivers.modules.amt import power as amt_power -from ironic.drivers.modules.amt import resource_uris -from ironic.tests.unit.conductor import mgr_utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.drivers.modules.amt import utils as test_utils -from ironic.tests.unit.objects import utils as obj_utils - -INFO_DICT = db_utils.get_test_amt_info() -CONF = cfg.CONF - - -class AMTPowerInteralMethodsTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTPowerInteralMethodsTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_amt') - self.info = INFO_DICT - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=self.info) - CONF.set_override('max_attempts', 2, 'amt') - CONF.set_override('action_wait', 0, 'amt') - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - @mock.patch.object(amt_common, 'get_wsman_client', spec_set=True, - autospec=True) - def test__set_power_state(self, mock_client_pywsman, mock_aw): - namespace = resource_uris.CIM_PowerManagementService - mock_client = mock_client_pywsman.return_value - amt_power._set_power_state(self.node, states.POWER_ON) - mock_client.wsman_invoke.assert_called_once_with( - mock.ANY, namespace, 'RequestPowerStateChange', mock.ANY) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - @mock.patch.object(amt_common, 'get_wsman_client', spec_set=True, - autospec=True) - def test__set_power_state_fail(self, mock_client_pywsman, mock_aw): - mock_client = mock_client_pywsman.return_value - mock_client.wsman_invoke.side_effect = exception.AMTFailure('x') - self.assertRaises(exception.AMTFailure, - amt_power._set_power_state, - self.node, states.POWER_ON) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - @mock.patch.object(amt_common, 'get_wsman_client', spec_set=True, - autospec=True) - def test__power_status(self, mock_gwc, mock_aw): - namespace = resource_uris.CIM_AssociatedPowerManagementService - result_xml = test_utils.build_soap_xml([{'PowerState': - '2'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_client = mock_gwc.return_value - mock_client.wsman_get.return_value = mock_doc - self.assertEqual( - states.POWER_ON, amt_power._power_status(self.node)) - - result_xml = test_utils.build_soap_xml([{'PowerState': - '8'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_client = mock_gwc.return_value - mock_client.wsman_get.return_value = mock_doc - self.assertEqual( - states.POWER_OFF, amt_power._power_status(self.node)) - - result_xml = test_utils.build_soap_xml([{'PowerState': - '4'}], - namespace) - mock_doc = test_utils.mock_wsman_root(result_xml) - mock_client = mock_gwc.return_value - mock_client.wsman_get.return_value = mock_doc - self.assertEqual( - states.ERROR, amt_power._power_status(self.node)) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_common, 'awake_amt_interface', spec_set=True, - autospec=True) - @mock.patch.object(amt_common, 'get_wsman_client', spec_set=True, - autospec=True) - def test__power_status_fail(self, mock_gwc, mock_aw): - mock_client = mock_gwc.return_value - mock_client.wsman_get.side_effect = exception.AMTFailure('x') - self.assertRaises(exception.AMTFailure, - amt_power._power_status, - self.node) - self.assertTrue(mock_aw.called) - - @mock.patch.object(amt_mgmt.AMTManagement, 'ensure_next_boot_device', - spec_set=True, autospec=True) - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - @mock.patch.object(amt_power, '_set_power_state', spec_set=True, - autospec=True) - def test__set_and_wait_power_on_with_boot_device(self, mock_sps, - mock_ps, mock_enbd): - target_state = states.POWER_ON - boot_device = boot_devices.PXE - mock_ps.side_effect = [states.POWER_OFF, states.POWER_ON] - mock_enbd.return_value = None - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.node.driver_internal_info['amt_boot_device'] = boot_device - result = amt_power._set_and_wait(task, target_state) - self.assertEqual(states.POWER_ON, result) - mock_enbd.assert_called_with(task.driver.management, task.node, - boot_devices.PXE) - mock_sps.assert_called_once_with(task.node, states.POWER_ON) - mock_ps.assert_called_with(task.node) - - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - @mock.patch.object(amt_power, '_set_power_state', spec_set=True, - autospec=True) - def test__set_and_wait_power_on_without_boot_device(self, mock_sps, - mock_ps): - target_state = states.POWER_ON - mock_ps.side_effect = [states.POWER_OFF, states.POWER_ON] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(states.POWER_ON, - amt_power._set_and_wait(task, target_state)) - mock_sps.assert_called_once_with(task.node, states.POWER_ON) - mock_ps.assert_called_with(task.node) - - boot_device = boot_devices.DISK - self.node.driver_internal_info['amt_boot_device'] = boot_device - mock_ps.side_effect = [states.POWER_OFF, states.POWER_ON] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(states.POWER_ON, - amt_power._set_and_wait(task, target_state)) - mock_sps.assert_called_with(task.node, states.POWER_ON) - mock_ps.assert_called_with(task.node) - - def test__set_and_wait_wrong_target_state(self): - target_state = 'fake-state' - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertRaises(exception.InvalidParameterValue, - amt_power._set_and_wait, task, target_state) - - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - @mock.patch.object(amt_power, '_set_power_state', spec_set=True, - autospec=True) - def test__set_and_wait_exceed_iterations(self, mock_sps, - mock_ps): - target_state = states.POWER_ON - mock_ps.side_effect = [states.POWER_OFF, states.POWER_OFF, - states.POWER_OFF] - mock_sps.return_value = exception.AMTFailure('x') - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertRaises(exception.PowerStateFailure, - amt_power._set_and_wait, task, target_state) - mock_sps.assert_called_with(task.node, states.POWER_ON) - mock_ps.assert_called_with(task.node) - self.assertEqual(3, mock_ps.call_count) - - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - def test__set_and_wait_already_target_state(self, mock_ps): - target_state = states.POWER_ON - mock_ps.side_effect = [states.POWER_ON] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(states.POWER_ON, - amt_power._set_and_wait(task, target_state)) - mock_ps.assert_called_with(task.node) - - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - @mock.patch.object(amt_power, '_set_power_state', spec_set=True, - autospec=True) - def test__set_and_wait_power_off(self, mock_sps, mock_ps): - target_state = states.POWER_OFF - mock_ps.side_effect = [states.POWER_ON, states.POWER_OFF] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(states.POWER_OFF, - amt_power._set_and_wait(task, target_state)) - mock_sps.assert_called_once_with(task.node, states.POWER_OFF) - mock_ps.assert_called_with(task.node) - - -class AMTPowerTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTPowerTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_amt') - self.info = INFO_DICT - self.node = obj_utils.create_test_node(self.context, - driver='fake_amt', - driver_info=self.info) - - def test_get_properties(self): - expected = amt_common.COMMON_PROPERTIES - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertEqual(expected, task.driver.get_properties()) - - @mock.patch.object(amt_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test_validate(self, mock_drvinfo): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.power.validate(task) - mock_drvinfo.assert_called_once_with(task.node) - - @mock.patch.object(amt_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test_validate_fail(self, mock_drvinfo): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - mock_drvinfo.side_effect = exception.InvalidParameterValue('x') - self.assertRaises(exception.InvalidParameterValue, - task.driver.power.validate, - task) - - @mock.patch.object(amt_power, '_power_status', spec_set=True, - autospec=True) - def test_get_power_state(self, mock_ps): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - mock_ps.return_value = states.POWER_ON - self.assertEqual(states.POWER_ON, - task.driver.power.get_power_state(task)) - mock_ps.assert_called_once_with(task.node) - - @mock.patch.object(amt_power, '_set_and_wait', spec_set=True, - autospec=True) - def test_set_power_state(self, mock_saw): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - pstate = states.POWER_ON - mock_saw.return_value = states.POWER_ON - task.driver.power.set_power_state(task, pstate) - mock_saw.assert_called_once_with(task, pstate) - - @mock.patch.object(amt_power, '_set_and_wait', spec_set=True, - autospec=True) - def test_set_power_state_fail(self, mock_saw): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - pstate = states.POWER_ON - mock_saw.side_effect = exception.PowerStateFailure('x') - self.assertRaises(exception.PowerStateFailure, - task.driver.power.set_power_state, - task, pstate) - mock_saw.assert_called_once_with(task, pstate) - - @mock.patch.object(amt_power, '_set_and_wait', spec_set=True, - autospec=True) - def test_reboot(self, mock_saw): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.power.reboot(task) - calls = [mock.call(task, states.POWER_OFF), - mock.call(task, states.POWER_ON)] - mock_saw.assert_has_calls(calls) diff --git a/ironic/tests/unit/drivers/modules/amt/test_vendor.py b/ironic/tests/unit/drivers/modules/amt/test_vendor.py deleted file mode 100644 index f8dbb11885..0000000000 --- a/ironic/tests/unit/drivers/modules/amt/test_vendor.py +++ /dev/null @@ -1,90 +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. - -"""Test class for AMT Vendor methods.""" - -import mock - -from ironic.common import boot_devices -from ironic.common import states -from ironic.conductor import task_manager -from ironic.drivers.modules.amt import management as amt_mgmt -from ironic.drivers.modules import iscsi_deploy -from ironic.tests.unit.conductor import mgr_utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.objects import utils as obj_utils - -INFO_DICT = db_utils.get_test_amt_info() - - -class AMTPXEVendorPassthruTestCase(db_base.DbTestCase): - - def setUp(self): - super(AMTPXEVendorPassthruTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver="pxe_amt") - self.node = obj_utils.create_test_node( - self.context, driver='pxe_amt', driver_info=INFO_DICT) - - def test_vendor_routes(self): - expected = ['heartbeat'] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - vendor_routes = task.driver.vendor.vendor_routes - self.assertIsInstance(vendor_routes, dict) - self.assertEqual(sorted(expected), sorted(list(vendor_routes))) - - def test_driver_routes(self): - expected = ['lookup'] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - driver_routes = task.driver.vendor.driver_routes - self.assertIsInstance(driver_routes, dict) - self.assertEqual(sorted(expected), sorted(list(driver_routes))) - - @mock.patch.object(amt_mgmt.AMTManagement, 'ensure_next_boot_device', - spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy.VendorPassthru, 'continue_deploy', - spec_set=True, autospec=True) - def test_vendorpassthru_continue_deploy_netboot(self, - mock_pxe_vendorpassthru, - mock_ensure): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.provision_state = states.DEPLOYWAIT - task.node.target_provision_state = states.ACTIVE - task.node.instance_info['capabilities'] = { - "boot_option": "netboot" - } - task.driver.vendor.continue_deploy(task) - mock_ensure.assert_called_with( - task.driver.management, task.node, boot_devices.PXE) - mock_pxe_vendorpassthru.assert_called_once_with( - task.driver.vendor, task) - - @mock.patch.object(amt_mgmt.AMTManagement, 'ensure_next_boot_device', - spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy.VendorPassthru, 'continue_deploy', - spec_set=True, autospec=True) - def test_vendorpassthru_continue_deploy_localboot(self, - mock_pxe_vendorpassthru, - mock_ensure): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.provision_state = states.DEPLOYWAIT - task.node.target_provision_state = states.ACTIVE - task.node.instance_info['capabilities'] = {"boot_option": "local"} - task.driver.vendor.continue_deploy(task) - self.assertFalse(mock_ensure.called) - mock_pxe_vendorpassthru.assert_called_once_with( - task.driver.vendor, task) diff --git a/ironic/tests/unit/drivers/modules/amt/utils.py b/ironic/tests/unit/drivers/modules/amt/utils.py deleted file mode 100644 index 552d3d37b3..0000000000 --- a/ironic/tests/unit/drivers/modules/amt/utils.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2014 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from xml import etree - -import mock - - -def build_soap_xml(items, namespace=None): - """Build a SOAP XML. - - :param items: a list of dictionaries where key is the element name - and the value is the element text. - :param namespace: the namespace for the elements, None for no - namespace. Defaults to None - :returns: a XML string. - - """ - - def _create_element(name, value=None): - xml_string = name - if namespace: - xml_string = "{%(namespace)s}%(item)s" % {'namespace': namespace, - 'item': xml_string} - - element = etree.ElementTree.Element(xml_string) - element.text = value - return element - - soap_namespace = "http://www.w3.org/2003/05/soap-envelope" - envelope_element = etree.ElementTree.Element( - "{%s}Envelope" % soap_namespace) - body_element = etree.ElementTree.Element("{%s}Body" % soap_namespace) - - for item in items: - for i in item: - insertion_point = _create_element(i) - if isinstance(item[i], dict): - for j, value in item[i].items(): - insertion_point.append(_create_element(j, value)) - else: - insertion_point.text = item[i] - - body_element.append(insertion_point) - - envelope_element.append(body_element) - return etree.ElementTree.tostring(envelope_element) - - -def mock_wsman_root(return_value): - """Helper function to mock the root() from wsman client.""" - mock_xml_root = mock.Mock(spec_set=['string']) - mock_xml_root.string.return_value = return_value - - mock_xml = mock.Mock(spec_set=['context', 'root']) - mock_xml.context.return_value = None - mock_xml.root.return_value = mock_xml_root - - return mock_xml diff --git a/ironic/tests/unit/drivers/modules/test_iboot.py b/ironic/tests/unit/drivers/modules/test_iboot.py deleted file mode 100644 index bcdb3286f6..0000000000 --- a/ironic/tests/unit/drivers/modules/test_iboot.py +++ /dev/null @@ -1,411 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2014 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Test class for iBoot PDU driver module.""" - -import types - -import mock - -from ironic.common import driver_factory -from ironic.common import exception -from ironic.common import states -from ironic.conductor import task_manager -from ironic.drivers.modules import iboot -from ironic.tests.unit.conductor import mgr_utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.objects import utils as obj_utils - - -INFO_DICT = db_utils.get_test_iboot_info() - - -class IBootPrivateMethodTestCase(db_base.DbTestCase): - - def setUp(self): - super(IBootPrivateMethodTestCase, self).setUp() - self.config(max_retry=0, group='iboot') - self.config(retry_interval=0, group='iboot') - - def test__parse_driver_info_good(self): - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - self.assertEqual(INFO_DICT['iboot_address'], info['address']) - self.assertEqual(INFO_DICT['iboot_username'], info['username']) - self.assertEqual(INFO_DICT['iboot_password'], info['password']) - self.assertEqual(9100, info['port']) - self.assertEqual(1, info['relay_id']) - - def test__parse_driver_info_good_with_explicit_port(self): - info = dict(INFO_DICT) - info['iboot_port'] = '1234' - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - info = iboot._parse_driver_info(node) - self.assertEqual(1234, info['port']) - - def test__parse_driver_info_good_with_explicit_relay_id(self): - info = dict(INFO_DICT) - info['iboot_relay_id'] = '2' - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - info = iboot._parse_driver_info(node) - self.assertEqual(2, info['relay_id']) - - def test__parse_driver_info_missing_address(self): - info = dict(INFO_DICT) - del info['iboot_address'] - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - self.assertRaises(exception.MissingParameterValue, - iboot._parse_driver_info, - node) - - def test__parse_driver_info_missing_username(self): - info = dict(INFO_DICT) - del info['iboot_username'] - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - self.assertRaises(exception.MissingParameterValue, - iboot._parse_driver_info, - node) - - def test__parse_driver_info_missing_password(self): - info = dict(INFO_DICT) - del info['iboot_password'] - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - self.assertRaises(exception.MissingParameterValue, - iboot._parse_driver_info, - node) - - def test__parse_driver_info_bad_port(self): - info = dict(INFO_DICT) - info['iboot_port'] = 'not-integer' - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - self.assertRaises(exception.InvalidParameterValue, - iboot._parse_driver_info, - node) - - def test__parse_driver_info_bad_relay_id(self): - info = dict(INFO_DICT) - info['iboot_relay_id'] = 'not-integer' - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=info) - self.assertRaises(exception.InvalidParameterValue, - iboot._parse_driver_info, - node) - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_on(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - mock_connection.get_relays.return_value = [True] - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - - self.assertEqual(states.POWER_ON, status) - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_off(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - mock_connection.get_relays.return_value = [False] - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - - self.assertEqual(states.POWER_OFF, status) - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_exception(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - mock_connection.get_relays.return_value = None - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - self.assertEqual(states.ERROR, status) - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_exception_type_error(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - side_effect = TypeError("Surprise!") - mock_connection.get_relays.side_effect = side_effect - - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - self.assertEqual(states.ERROR, status) - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_exception_index_error(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - side_effect = IndexError("Gotcha!") - mock_connection.get_relays.side_effect = side_effect - - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - status = iboot._power_status(info) - self.assertEqual(states.ERROR, status) - - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_error(self, mock_get_conn): - mock_connection = mock.MagicMock(spec_set=['get_relays']) - mock_connection.get_relays.return_value = list() - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - - self.assertEqual(states.ERROR, status) - mock_get_conn.assert_called_once_with(info) - mock_connection.get_relays.assert_called_once_with() - - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__power_status_retries(self, mock_get_conn): - self.config(max_retry=1, group='iboot') - - mock_connection = mock.MagicMock(spec_set=['get_relays']) - side_effect = TypeError("Surprise!") - mock_connection.get_relays.side_effect = side_effect - - mock_get_conn.return_value = mock_connection - node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - info = iboot._parse_driver_info(node) - - status = iboot._power_status(info) - self.assertEqual(states.ERROR, status) - mock_get_conn.assert_called_once_with(info) - self.assertEqual(2, mock_connection.get_relays.call_count) - - -class IBootDriverTestCase(db_base.DbTestCase): - - def setUp(self): - super(IBootDriverTestCase, self).setUp() - self.config(max_retry=0, group='iboot') - self.config(retry_interval=0, group='iboot') - self.config(reboot_delay=0, group='iboot') - mgr_utils.mock_the_extension_manager(driver='fake_iboot') - self.driver = driver_factory.get_driver('fake_iboot') - self.node = obj_utils.create_test_node( - self.context, - driver='fake_iboot', - driver_info=INFO_DICT) - self.info = iboot._parse_driver_info(self.node) - - def test_get_properties(self): - expected = iboot.COMMON_PROPERTIES - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - self.assertEqual(expected, task.driver.get_properties()) - - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', autospec=True) - def test_set_power_state_good(self, mock_switch, mock_power_status): - mock_power_status.return_value = states.POWER_ON - - with task_manager.acquire(self.context, self.node.uuid) as task: - task.driver.power.set_power_state(task, states.POWER_ON) - - # ensure functions were called with the valid parameters - mock_switch.assert_called_once_with(self.info, True) - mock_power_status.assert_called_once_with(self.info) - - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', autospec=True) - def test_set_power_state_bad(self, mock_switch, mock_power_status): - mock_power_status.return_value = states.POWER_OFF - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaises(exception.PowerStateFailure, - task.driver.power.set_power_state, - task, states.POWER_ON) - - # ensure functions were called with the valid parameters - mock_switch.assert_called_once_with(self.info, True) - mock_power_status.assert_called_once_with(self.info) - - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', autospec=True) - def test_set_power_state_retry(self, mock_switch, mock_power_status): - self.config(max_retry=2, group='iboot') - mock_power_status.return_value = states.POWER_OFF - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaises(exception.PowerStateFailure, - task.driver.power.set_power_state, - task, states.POWER_ON) - - # ensure functions were called with the valid parameters - mock_switch.assert_called_once_with(self.info, True) - # 1 + 2 retries - self.assertEqual(3, mock_power_status.call_count) - - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', autospec=True) - def test_set_power_state_invalid_parameter(self, mock_switch, - mock_power_status): - mock_power_status.return_value = states.POWER_ON - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaises(exception.InvalidParameterValue, - task.driver.power.set_power_state, - task, states.NOSTATE) - - @mock.patch.object(iboot, '_sleep_switch', spec_set=types.FunctionType) - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', spec_set=types.FunctionType) - def test_reboot_good(self, mock_switch, mock_power_status, - mock_sleep_switch): - self.config(reboot_delay=3, group='iboot') - manager = mock.MagicMock(spec_set=['switch', 'sleep']) - mock_power_status.return_value = states.POWER_ON - - manager.attach_mock(mock_switch, 'switch') - manager.attach_mock(mock_sleep_switch, 'sleep') - expected = [mock.call.switch(self.info, False), - mock.call.sleep(3), - mock.call.switch(self.info, True)] - - with task_manager.acquire(self.context, self.node.uuid) as task: - task.driver.power.reboot(task) - - self.assertEqual(manager.mock_calls, expected) - - @mock.patch.object(iboot, '_sleep_switch', spec_set=types.FunctionType) - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_switch', spec_set=types.FunctionType) - def test_reboot_bad(self, mock_switch, mock_power_status, - mock_sleep_switch): - self.config(reboot_delay=3, group='iboot') - manager = mock.MagicMock(spec_set=['switch', 'sleep']) - mock_power_status.return_value = states.POWER_OFF - - manager.attach_mock(mock_switch, 'switch') - manager.attach_mock(mock_sleep_switch, 'sleep') - expected = [mock.call.switch(self.info, False), - mock.call.sleep(3), - mock.call.switch(self.info, True)] - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaises(exception.PowerStateFailure, - task.driver.power.reboot, task) - - self.assertEqual(manager.mock_calls, expected) - - @mock.patch.object(iboot, '_power_status', autospec=True) - @mock.patch.object(iboot, '_get_connection', autospec=True) - def test__switch_retries(self, mock_get_conn, mock_power_status): - self.config(max_retry=1, group='iboot') - mock_power_status.return_value = states.POWER_ON - - mock_connection = mock.MagicMock(spec_set=['switch']) - side_effect = TypeError("Surprise!") - mock_connection.switch.side_effect = side_effect - mock_get_conn.return_value = mock_connection - - iboot._switch(self.info, False) - self.assertEqual(2, mock_connection.switch.call_count) - - @mock.patch.object(iboot, '_power_status', autospec=True) - def test_get_power_state(self, mock_power_status): - mock_power_status.return_value = states.POWER_ON - - with task_manager.acquire(self.context, self.node.uuid) as task: - state = task.driver.power.get_power_state(task) - self.assertEqual(state, states.POWER_ON) - - # ensure functions were called with the valid parameters - mock_power_status.assert_called_once_with(self.info) - - @mock.patch.object(iboot, '_parse_driver_info', autospec=True) - def test_validate_good(self, parse_drv_info_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.power.validate(task) - self.assertEqual(1, parse_drv_info_mock.call_count) - - @mock.patch.object(iboot, '_parse_driver_info', autospec=True) - def test_validate_fails(self, parse_drv_info_mock): - side_effect = exception.InvalidParameterValue("Bad input") - parse_drv_info_mock.side_effect = side_effect - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertRaises(exception.InvalidParameterValue, - task.driver.power.validate, task) - self.assertEqual(1, parse_drv_info_mock.call_count) diff --git a/ironic/tests/unit/drivers/modules/test_wol.py b/ironic/tests/unit/drivers/modules/test_wol.py deleted file mode 100644 index a343f34240..0000000000 --- a/ironic/tests/unit/drivers/modules/test_wol.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2015 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Test class for Wake-On-Lan driver module.""" - -import socket -import time - -import mock -from oslo_utils import uuidutils - -from ironic.common import driver_factory -from ironic.common import exception -from ironic.common import states -from ironic.conductor import task_manager -from ironic.drivers.modules import wol -from ironic.tests.unit.conductor import mgr_utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.objects import utils as obj_utils - - -@mock.patch.object(time, 'sleep', lambda *_: None) -class WakeOnLanPrivateMethodTestCase(db_base.DbTestCase): - - def setUp(self): - super(WakeOnLanPrivateMethodTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_wol') - self.driver = driver_factory.get_driver('fake_wol') - self.node = obj_utils.create_test_node(self.context, - driver='fake_wol') - self.port = obj_utils.create_test_port(self.context, - node_id=self.node.id) - - def test__parse_parameters(self): - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - params = wol._parse_parameters(task) - self.assertEqual('255.255.255.255', params['host']) - self.assertEqual(9, params['port']) - - def test__parse_parameters_non_default_params(self): - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - task.node.driver_info = {'wol_host': '1.2.3.4', - 'wol_port': 7} - params = wol._parse_parameters(task) - self.assertEqual('1.2.3.4', params['host']) - self.assertEqual(7, params['port']) - - def test__parse_parameters_no_ports_fail(self): - node = obj_utils.create_test_node( - self.context, - uuid=uuidutils.generate_uuid(), - driver='fake_wol') - with task_manager.acquire( - self.context, node.uuid, shared=True) as task: - self.assertRaises(exception.InvalidParameterValue, - wol._parse_parameters, task) - - @mock.patch.object(socket, 'socket', autospec=True, spec_set=True) - def test_send_magic_packets(self, mock_socket): - fake_socket = mock.Mock(spec=socket, spec_set=True) - mock_socket.return_value = fake_socket() - obj_utils.create_test_port(self.context, - uuid=uuidutils.generate_uuid(), - address='aa:bb:cc:dd:ee:ff', - node_id=self.node.id) - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - wol._send_magic_packets(task, '255.255.255.255', 9) - - expected_calls = [ - mock.call(), - mock.call().setsockopt(socket.SOL_SOCKET, - socket.SO_BROADCAST, 1), - mock.call().sendto(mock.ANY, ('255.255.255.255', 9)), - mock.call().sendto(mock.ANY, ('255.255.255.255', 9)), - mock.call().close()] - - fake_socket.assert_has_calls(expected_calls) - self.assertEqual(1, mock_socket.call_count) - - @mock.patch.object(socket, 'socket', autospec=True, spec_set=True) - def test_send_magic_packets_network_sendto_error(self, mock_socket): - fake_socket = mock.Mock(spec=socket, spec_set=True) - fake_socket.return_value.sendto.side_effect = socket.error('boom') - mock_socket.return_value = fake_socket() - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - self.assertRaises(exception.WolOperationError, - wol._send_magic_packets, - task, '255.255.255.255', 9) - self.assertEqual(1, mock_socket.call_count) - # assert sendt0() was invoked - fake_socket.return_value.sendto.assert_called_once_with( - mock.ANY, ('255.255.255.255', 9)) - - @mock.patch.object(socket, 'socket', autospec=True, spec_set=True) - def test_magic_packet_format(self, mock_socket): - fake_socket = mock.Mock(spec=socket, spec_set=True) - mock_socket.return_value = fake_socket() - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - wol._send_magic_packets(task, '255.255.255.255', 9) - - expct_packet = (b'\xff\xff\xff\xff\xff\xffRT\x00\xcf-1RT\x00' - b'\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT' - b'\x00\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT\x00' - b'\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT' - b'\x00\xcf-1RT\x00\xcf-1RT\x00\xcf-1RT\x00\xcf-1') - mock_socket.return_value.sendto.assert_called_once_with( - expct_packet, ('255.255.255.255', 9)) - - -@mock.patch.object(time, 'sleep', lambda *_: None) -class WakeOnLanDriverTestCase(db_base.DbTestCase): - - def setUp(self): - super(WakeOnLanDriverTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver='fake_wol') - self.driver = driver_factory.get_driver('fake_wol') - self.node = obj_utils.create_test_node(self.context, - driver='fake_wol') - self.port = obj_utils.create_test_port(self.context, - node_id=self.node.id) - - def test_get_properties(self): - expected = wol.COMMON_PROPERTIES - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - self.assertEqual(expected, task.driver.get_properties()) - - def test_get_power_state(self): - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - task.node.power_state = states.POWER_ON - pstate = task.driver.power.get_power_state(task) - self.assertEqual(states.POWER_ON, pstate) - - def test_get_power_state_nostate(self): - with task_manager.acquire( - self.context, self.node.uuid, shared=True) as task: - task.node.power_state = states.NOSTATE - pstate = task.driver.power.get_power_state(task) - self.assertEqual(states.POWER_OFF, pstate) - - @mock.patch.object(wol, '_send_magic_packets', autospec=True, - spec_set=True) - def test_set_power_state_power_on(self, mock_magic): - with task_manager.acquire(self.context, self.node.uuid) as task: - task.driver.power.set_power_state(task, states.POWER_ON) - mock_magic.assert_called_once_with(task, '255.255.255.255', 9) - - @mock.patch.object(wol.LOG, 'info', autospec=True, spec_set=True) - @mock.patch.object(wol, '_send_magic_packets', autospec=True, - spec_set=True) - def test_set_power_state_power_off(self, mock_magic, mock_log): - with task_manager.acquire(self.context, self.node.uuid) as task: - task.driver.power.set_power_state(task, states.POWER_OFF) - mock_log.assert_called_once_with(mock.ANY, self.node.uuid) - # assert magic packets weren't sent - self.assertFalse(mock_magic.called) - - @mock.patch.object(wol, '_send_magic_packets', autospec=True, - spec_set=True) - def test_set_power_state_power_fail(self, mock_magic): - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaises(exception.InvalidParameterValue, - task.driver.power.set_power_state, - task, 'wrong-state') - # assert magic packets weren't sent - self.assertFalse(mock_magic.called) - - @mock.patch.object(wol.LOG, 'info', autospec=True, spec_set=True) - @mock.patch.object(wol.WakeOnLanPower, 'set_power_state', autospec=True, - spec_set=True) - def test_reboot(self, mock_power, mock_log): - with task_manager.acquire(self.context, self.node.uuid) as task: - task.driver.power.reboot(task) - mock_log.assert_called_once_with(mock.ANY, self.node.uuid) - mock_power.assert_called_once_with(task.driver.power, task, - states.POWER_ON) diff --git a/ironic/tests/unit/drivers/test_agent.py b/ironic/tests/unit/drivers/test_agent.py index 1f9913e4fa..13b7579835 100644 --- a/ironic/tests/unit/drivers/test_agent.py +++ b/ironic/tests/unit/drivers/test_agent.py @@ -16,18 +16,10 @@ Test class for Agent Deploy Driver """ -import mock -import testtools - -from ironic.common import exception from ironic.drivers import agent from ironic.drivers.modules import agent as agent_module -from ironic.drivers.modules.amt import management as amt_management -from ironic.drivers.modules.amt import power as amt_power -from ironic.drivers.modules import iboot from ironic.drivers.modules import ipmitool from ironic.drivers.modules import pxe -from ironic.drivers.modules import wol from ironic.tests import base @@ -57,47 +49,3 @@ class AgentAndIPMIToolAndSocatDriverTestCase(base.TestCase): self.assertIsInstance(driver.management, ipmitool.IPMIManagement) self.assertIsInstance(driver.vendor, ipmitool.VendorPassthru) self.assertIsInstance(driver.raid, agent_module.AgentRAID) - - -class AgentAndAMTDriverTestCase(testtools.TestCase): - - @mock.patch.object(agent.importutils, 'try_import', spec_set=True, - autospec=True) - def test___init__(self, mock_try_import): - mock_try_import.return_value = True - driver = agent.AgentAndAMTDriver() - - self.assertIsInstance(driver.power, amt_power.AMTPower) - self.assertIsInstance(driver.boot, pxe.PXEBoot) - self.assertIsInstance(driver.deploy, agent_module.AgentDeploy) - self.assertIsInstance(driver.management, amt_management.AMTManagement) - self.assertIsInstance(driver.vendor, agent_module.AgentVendorInterface) - - @mock.patch.object(agent.importutils, 'try_import') - def test___init___try_import_exception(self, mock_try_import): - mock_try_import.return_value = False - - self.assertRaises(exception.DriverLoadError, - agent.AgentAndAMTDriver) - - -class AgentAndWakeOnLanDriverTestCase(testtools.TestCase): - - def test___init__(self): - driver = agent.AgentAndWakeOnLanDriver() - - self.assertIsInstance(driver.power, wol.WakeOnLanPower) - self.assertIsInstance(driver.boot, pxe.PXEBoot) - self.assertIsInstance(driver.deploy, agent_module.AgentDeploy) - self.assertIsInstance(driver.vendor, agent_module.AgentVendorInterface) - - -class AgentAndIBootDriverTestCase(testtools.TestCase): - - def test___init__(self): - driver = agent.AgentAndIBootDriver() - - self.assertIsInstance(driver.power, iboot.IBootPower) - self.assertIsInstance(driver.boot, pxe.PXEBoot) - self.assertIsInstance(driver.deploy, agent_module.AgentDeploy) - self.assertIsInstance(driver.vendor, agent_module.AgentVendorInterface) diff --git a/ironic/tests/unit/drivers/test_pxe.py b/ironic/tests/unit/drivers/test_pxe.py index 2fe9ec2dfd..f3f6fcba5b 100644 --- a/ironic/tests/unit/drivers/test_pxe.py +++ b/ironic/tests/unit/drivers/test_pxe.py @@ -21,12 +21,8 @@ import testtools from ironic.common import exception from ironic.drivers.modules import agent -from ironic.drivers.modules.amt import management as amt_management -from ironic.drivers.modules.amt import power as amt_power -from ironic.drivers.modules.amt import vendor as amt_vendor from ironic.drivers.modules.cimc import management as cimc_management from ironic.drivers.modules.cimc import power as cimc_power -from ironic.drivers.modules import iboot from ironic.drivers.modules.ilo import console as ilo_console from ironic.drivers.modules.ilo import inspect as ilo_inspect from ironic.drivers.modules.ilo import management as ilo_management @@ -46,7 +42,6 @@ from ironic.drivers.modules import ssh from ironic.drivers.modules.ucs import management as ucs_management from ironic.drivers.modules.ucs import power as ucs_power from ironic.drivers.modules import virtualbox -from ironic.drivers.modules import wol from ironic.drivers import pxe @@ -134,26 +129,6 @@ class PXEDriversTestCase(testtools.TestCase): self.assertRaises(exception.DriverLoadError, pxe.PXEAndSeaMicroDriver) - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, - autospec=True) - def test_pxe_iboot_driver(self, try_import_mock): - try_import_mock.return_value = True - - driver = pxe.PXEAndIBootDriver() - - self.assertIsInstance(driver.power, iboot.IBootPower) - self.assertIsInstance(driver.boot, pxe_module.PXEBoot) - self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy) - self.assertIsInstance(driver.vendor, iscsi_deploy.VendorPassthru) - - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, - autospec=True) - def test_pxe_iboot_driver_import_error(self, try_import_mock): - try_import_mock.return_value = False - - self.assertRaises(exception.DriverLoadError, - pxe.PXEAndIBootDriver) - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, autospec=True) def test_pxe_ilo_driver(self, try_import_mock): @@ -244,28 +219,6 @@ class PXEDriversTestCase(testtools.TestCase): self.assertRaises(exception.DriverLoadError, pxe.PXEAndVirtualBoxDriver) - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, - autospec=True) - def test_pxe_amt_driver(self, try_import_mock): - try_import_mock.return_value = True - - driver = pxe.PXEAndAMTDriver() - - self.assertIsInstance(driver.power, amt_power.AMTPower) - self.assertIsInstance(driver.boot, pxe_module.PXEBoot) - self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy) - self.assertIsInstance(driver.management, - amt_management.AMTManagement) - self.assertIsInstance(driver.vendor, amt_vendor.AMTPXEVendorPassthru) - - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, - autospec=True) - def test_pxe_amt_driver_import_error(self, try_import_mock): - try_import_mock.return_value = False - - self.assertRaises(exception.DriverLoadError, - pxe.PXEAndAMTDriver) - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, autospec=True) def test_pxe_msftocs_driver(self, try_import_mock): @@ -320,15 +273,3 @@ class PXEDriversTestCase(testtools.TestCase): self.assertRaises(exception.DriverLoadError, pxe.PXEAndCIMCDriver) - - @mock.patch.object(pxe.importutils, 'try_import', spec_set=True, - autospec=True) - def test_pxe_wakeonlan_driver(self, try_import_mock): - try_import_mock.return_value = True - - driver = pxe.PXEAndWakeOnLanDriver() - - self.assertIsInstance(driver.power, wol.WakeOnLanPower) - self.assertIsInstance(driver.boot, pxe_module.PXEBoot) - self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy) - self.assertIsInstance(driver.vendor, iscsi_deploy.VendorPassthru) diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index 62500600fc..0519e8c8ab 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -33,11 +33,6 @@ DRACCLIENT_CONSTANTS_MOD_SPEC = ( 'REBOOT' ) -# iboot -IBOOT_SPEC = ( - 'iBootInterface', -) - # ironic_inspector IRONIC_INSPECTOR_CLIENT_SPEC = ( 'ClientV1', diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index 7490e39133..22aa8e76c8 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -134,17 +134,6 @@ if not oneview_client: six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview']) -# attempt to load the external 'pywsman' library, which is required by -# the optional drivers.modules.amt module -pywsman = importutils.try_import('pywsman') -if not pywsman: - pywsman = mock.MagicMock(spec_set=mock_specs.PYWSMAN_SPEC) - sys.modules['pywsman'] = pywsman - # Now that the external library has been mocked, if anything had already - # loaded any of the drivers, reload them. - if 'ironic.drivers.modules.amt' in sys.modules: - six.moves.reload_module(sys.modules['ironic.drivers.modules.amt']) - # attempt to load the external 'python-dracclient' library, which is required # by the optional drivers.modules.drac module dracclient = importutils.try_import('dracclient') @@ -168,20 +157,6 @@ if not dracclient: if 'ironic.drivers.modules.drac' in sys.modules: six.moves.reload_module(sys.modules['ironic.drivers.modules.drac']) -# attempt to load the external 'iboot' library, which is required by -# the optional drivers.modules.iboot module -iboot = importutils.try_import("iboot") -if not iboot: - ib = mock.MagicMock(spec_set=mock_specs.IBOOT_SPEC) - ib.iBootInterface = mock.MagicMock(spec_set=[]) - sys.modules['iboot'] = ib - -# if anything has loaded the iboot driver yet, reload it now that the -# external library has been mocked -if 'ironic.drivers.modules.iboot' in sys.modules: - six.moves.reload_module(sys.modules['ironic.drivers.modules.iboot']) - - # attempt to load the external 'pysnmp' library, which is required by # the optional drivers.modules.snmp module pysnmp = importutils.try_import("pysnmp") diff --git a/releasenotes/notes/remove-iboot-wol-amt-27edaf109d28b663.yaml b/releasenotes/notes/remove-iboot-wol-amt-27edaf109d28b663.yaml new file mode 100644 index 0000000000..74f0accab9 --- /dev/null +++ b/releasenotes/notes/remove-iboot-wol-amt-27edaf109d28b663.yaml @@ -0,0 +1,18 @@ +--- +upgrades: + - | + iBoot, Wake-On-LAN and AMT drivers are removed from ironic as they + neither have nor are planning to have a third-party CI. + They are still available from unsupported ironic driver collection + in ``ironic-staging-drivers`` repository. + If the ironic installation was using any driver based on those, + the operator has to install ``ironic-staging-drivers`` + and change the driver on affected nodes + according to following correspondence list + + * agent_amt -> pxe_amt_agent + * pxe_amt -> pxe_amt_iscsi + * agent_wol -> pxe_wol_agent + * pxe_wol -> pxe_wol_iscsi + * agent_iboot -> pxe_iboot_agent + * pxe_iboot -> pxe_iboot_iscsi diff --git a/setup.cfg b/setup.cfg index 5369d86a41..9514cf7ec4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,8 +42,6 @@ ironic.dhcp = none = ironic.dhcp.none:NoneDHCPApi ironic.drivers = - agent_amt = ironic.drivers.agent:AgentAndAMTDriver - agent_iboot = ironic.drivers.agent:AgentAndIBootDriver agent_ilo = ironic.drivers.ilo:IloVirtualMediaAgentDriver agent_ipmitool = ironic.drivers.agent:AgentAndIPMIToolDriver agent_ipmitool_socat = ironic.drivers.agent:AgentAndIPMIToolAndSocatDriver @@ -53,7 +51,6 @@ ironic.drivers = agent_ssh = ironic.drivers.agent:AgentAndSSHDriver agent_vbox = ironic.drivers.agent:AgentAndVirtualBoxDriver agent_ucs = ironic.drivers.agent:AgentAndUcsDriver - agent_wol = ironic.drivers.agent:AgentAndWakeOnLanDriver fake = ironic.drivers.fake:FakeDriver fake_agent = ironic.drivers.fake:FakeAgentDriver fake_inspector = ironic.drivers.fake:FakeIPMIToolInspectorDriver @@ -63,17 +60,14 @@ ironic.drivers = fake_ssh = ironic.drivers.fake:FakeSSHDriver fake_pxe = ironic.drivers.fake:FakePXEDriver fake_seamicro = ironic.drivers.fake:FakeSeaMicroDriver - fake_iboot = ironic.drivers.fake:FakeIBootDriver fake_ilo = ironic.drivers.fake:FakeIloDriver fake_drac = ironic.drivers.fake:FakeDracDriver fake_snmp = ironic.drivers.fake:FakeSNMPDriver fake_irmc = ironic.drivers.fake:FakeIRMCDriver fake_vbox = ironic.drivers.fake:FakeVirtualBoxDriver - fake_amt = ironic.drivers.fake:FakeAMTDriver fake_msftocs = ironic.drivers.fake:FakeMSFTOCSDriver fake_ucs = ironic.drivers.fake:FakeUcsDriver fake_cimc = ironic.drivers.fake:FakeCIMCDriver - fake_wol = ironic.drivers.fake:FakeWakeOnLanDriver fake_oneview = ironic.drivers.fake:FakeOneViewDriver iscsi_ilo = ironic.drivers.ilo:IloVirtualMediaIscsiDriver iscsi_irmc = ironic.drivers.irmc:IRMCVirtualMediaIscsiDriver @@ -84,16 +78,13 @@ ironic.drivers = pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver pxe_vbox = ironic.drivers.pxe:PXEAndVirtualBoxDriver pxe_seamicro = ironic.drivers.pxe:PXEAndSeaMicroDriver - pxe_iboot = ironic.drivers.pxe:PXEAndIBootDriver pxe_ilo = ironic.drivers.pxe:PXEAndIloDriver pxe_drac = ironic.drivers.drac:PXEDracDriver pxe_drac_inspector = ironic.drivers.drac:PXEDracInspectorDriver pxe_snmp = ironic.drivers.pxe:PXEAndSNMPDriver pxe_irmc = ironic.drivers.pxe:PXEAndIRMCDriver - pxe_amt = ironic.drivers.pxe:PXEAndAMTDriver pxe_msftocs = ironic.drivers.pxe:PXEAndMSFTOCSDriver pxe_ucs = ironic.drivers.pxe:PXEAndUcsDriver - pxe_wol = ironic.drivers.pxe:PXEAndWakeOnLanDriver pxe_iscsi_cimc = ironic.drivers.pxe:PXEAndCIMCDriver pxe_agent_cimc = ironic.drivers.agent:AgentAndCIMCDriver