Merge "Remove iBoot, WoL and AMT drivers"

This commit is contained in:
Jenkins 2016-12-16 22:16:58 +00:00 committed by Gerrit Code Review
commit d8e5a269ac
38 changed files with 32 additions and 3589 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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=<username> -i iboot_password=<password> -i iboot_address=<address>
References
==========
.. [1] iBoot-G2 official documentation - http://dataprobe.com/support_iboot-g2.html

View File

@ -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=<broadcast ip> [ -i
wol_port=<destination 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 <MAC address>
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

View File

@ -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

View File

@ -778,34 +778,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]
#
@ -1571,25 +1543,6 @@
#username = <None>
[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]
#

View File

@ -446,15 +446,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.")
@ -676,10 +667,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")

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -32,7 +32,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),
@ -44,7 +43,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),

View File

@ -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()

View File

@ -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. """

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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"),

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -33,11 +33,6 @@ DRACCLIENT_CONSTANTS_MOD_SPEC = (
'REBOOT'
)
# iboot
IBOOT_SPEC = (
'iBootInterface',
)
# ironic_inspector
IRONIC_INSPECTOR_CLIENT_SPEC = (
'ClientV1',

View File

@ -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")

View File

@ -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

View File

@ -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