Merge "Remove most unsupported drivers"

This commit is contained in:
Jenkins 2017-02-10 09:41:41 +00:00 committed by Gerrit Code Review
commit c1cc08702c
36 changed files with 39 additions and 3954 deletions

View File

@ -125,9 +125,8 @@ The web console can be configured in Bare Metal service in the following way:
ironic driver-properties <driver>
For ``*_ipmitool`` and ``*_ipminative`` drivers, this option is ``ipmi_terminal_port``.
For ``seamicro`` driver, this option is ``seamicro_terminal_port``. Give a customized port
number to ``<customized_port>``, for example ``8023``, this customized port is used in
web console url.
Give a customized port number to ``<customized_port>``,
for example ``8023``, this customized port is used in web console url.
Get web console information for a node as follows::

View File

@ -48,14 +48,6 @@ iLO driver
../drivers/ilo
SeaMicro driver
---------------
.. toctree::
:maxdepth: 1
../drivers/seamicro
iRMC driver
-----------
@ -64,15 +56,6 @@ iRMC driver
../drivers/irmc
VirtualBox driver
-----------------
.. toctree::
:maxdepth: 1
../drivers/vbox
Cisco UCS driver
----------------
@ -118,5 +101,8 @@ 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_
- Virtualbox drivers
- SeaMicro drivers
- MSFT OCS drivers
.. _ironic-staging-drivers: http://ironic-staging-drivers.readthedocs.io

View File

@ -1,117 +0,0 @@
.. _SeaMicro:
===============
SeaMicro driver
===============
Overview
========
The SeaMicro power driver enables you to take advantage of power cycle
management of servers (nodes) within the SeaMicro chassis. The SeaMicro
driver is targeted for SeaMicro Fabric Compute systems.
Prerequisites
=============
* ``python-seamicroclient`` is a python package which contains a set of modules
for managing SeaMicro Fabric Compute systems.
Install ``python-seamicroclient`` [1]_ module on the Ironic conductor node.
Minimum version required is 0.4.0::
$ pip install "python-seamicroclient>=0.4.0"
Drivers
=======
pxe_seamicro driver
^^^^^^^^^^^^^^^^^^^
Overview
~~~~~~~~
``pxe_seamicro`` driver uses PXE/iSCSI (just like ``pxe_ipmitool`` driver) to
deploy the image and uses SeaMicro to do all management operations on the
baremetal node (instead of using IPMI).
Target Users
~~~~~~~~~~~~
* Users who want to use PXE/iSCSI for deployment in their environment.
* Users who want to use SeaMicro Fabric Compute systems.
Tested Platforms
~~~~~~~~~~~~~~~~
This driver works on SeaMicro Fabric Compute system.
It has been tested with the following servers:
* SeaMicro SM15000-XN
* SeaMicro SM15000-OP
Requirements
~~~~~~~~~~~~
None.
Configuring and Enabling the driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Build or download a deploy image, see `Building or downloading a deploy ramdisk image`_.
2. Upload these images to Glance::
glance image-create --name deploy-ramdisk.kernel --disk-format aki --container-format aki < deploy-ramdisk.kernel
glance image-create --name deploy-ramdisk.initramfs --disk-format ari --container-format ari < deploy-ramdisk.initramfs
3. Add ``pxe_seamicro`` to the list of ``enabled_drivers`` in
``/etc/ironic/ironic.conf``. For example::
enabled_drivers = pxe_ipmitool,pxe_seamicro
4. Restart the Ironic conductor service::
service ironic-conductor restart
Registering SeaMicro node in Ironic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Nodes configured for SeaMicro driver should have the ``driver`` property set to
``pxe_seamicro``. The following configuration values are also required in
``driver_info``:
- ``seamicro_api_endpoint``: IP address or hostname of the SeaMicro with valid
URL as http://<IP_address/hostname>/v2.0
- ``seamicro_server_id``: SeaMicro Server ID. Expected format is <int>/<int>
- ``seamicro_username``: SeaMicro Username with administrator privileges.
- ``seamicro_password``: Password for the above SeaMicro user.
- ``deploy_kernel``: The Glance UUID of the deployment kernel.
- ``deploy_ramdisk``: The Glance UUID of the deployment ramdisk.
- ``seamicro_api_version``: (optional) SeaMicro API Version defaults to "2".
- ``seamicro_terminal_port``: (optional) Node's UDP port for console access.
Any unused port on the Ironic conductor node may be used.
The following sequence of commands can be used to enroll a SeaMicro node and
boot an instance on it:
Create nova baremetal flavor corresponding to SeaMicro server's config::
nova flavor-create baremetal auto <memory_size_in_MB> <disk_size_in_GB> <number_of_cpus>
Create Node::
ironic node-create -d pxe_seamicro -i seamicro_api_endpoint=https://<seamicro_ip_address>/ -i seamicro_server_id=<seamicro_server_id> -i seamicro_username=<seamicro_username> -i seamicro_password=<seamicro_password> -i seamicro_api_version=<seamicro_api_version> -i seamicro_terminal_port=<seamicro_terminal_port> -i deploy_kernel=<glance_uuid_of_deploy_kernel> -i deploy_ramdisk=<glance_uuid_of_deploy_ramdisk> -p cpus=<number_of_cpus> -p memory_mb=<memory_size_in_MB> -p local_gb=<local_disk_size_in_GB> -p cpu_arch=<cpu_arch>
Associate port with the node created::
ironic port-create -n $NODE -a <MAC_address_of_SeaMicro_server's_NIC>
Associate properties with the flavor::
nova flavor-key baremetal set "cpu_arch"=<cpu_arch>
Boot the Instance::
nova boot --flavor baremetal --image test-image instance-1
References
==========
.. [1] Python-seamicroclient - https://pypi.python.org/pypi/python-seamicroclient
.. [2] DiskImage-Builder - http://docs.openstack.org/developer/diskimage-builder/
.. _`Building or downloading a deploy ramdisk image`: http://docs.openstack.org/project-install-guide/baremetal/draft/deploy-ramdisk.html

View File

@ -1,121 +0,0 @@
.. _vbox:
==================
VirtualBox drivers
==================
Overview
========
VirtualBox drivers can be used to test Ironic by using VirtualBox VMs to
simulate bare metal nodes.
Ironic provides support via the ``pxe_ssh`` and ``agent_ssh`` drivers for using
a VirtualBox VM as a bare metal target and do provisioning on it. It works by
connecting via SSH into the VirtualBox host and running commands using
VBoxManage. This works well if you have VirtualBox installed on a Linux box.
But when VirtualBox is installed on a Windows box, configuring and getting SSH
to work with VBoxManage is difficult (if not impossible) due to the following
reasons:
* Windows doesn't come with native SSH support and one needs to use some
third-party software to enable SSH support on Windows.
* Even after configuring SSH, VBoxManage doesn't work remotely due to how
Windows manages user accounts -- the native Windows user account is different
from the corresponding SSH user account, and VBoxManage doesn't work
properly when done with the SSH user account.
* Even after tweaking the policies of the VirtualBox application, the remote
VBoxManage and VBoxSvc don't sync each other properly and often results in
a crash.
VirtualBox drivers use SOAP to talk to the VirtualBox web service running on
the VirtualBox host. These drivers are primarily intended for Ironic developers
running Windows on their laptops/desktops, although they can be used on other
operating systems as well. Using these drivers, a developer could configure a
cloud controller on one VirtualBox VM and use other VMs in the same VirtualBox
as bare metals for that cloud controller.
These VirtualBox drivers are available :
* ``pxe_vbox``: uses iSCSI-based deployment mechanism.
* ``agent_vbox``: uses agent-based deployment mechanism.
* ``fake_vbox``: uses VirtualBox for power and management, but uses fake
deploy.
Setting up development environment
==================================
* Install VirtualBox on your desktop or laptop.
* Create a VM for the cloud controller. Do not power on the VM now.
For example, ``cloud-controller``.
* In VirtualBox Manager, Select ``cloud-controller`` VM -> Click Settings ->
Network -> Adapter 2 -> Select 'Enable Network Adapter' ->
Select Attached to: Internal Network -> Select Name: intnet
* Create a VM in VirtualBox to act as bare metal. A VM with 1 CPU,
1 GB memory should be sufficient. Let's name this VM as ``baremetal``.
* In VirtualBox Manager, Select ``baremetal`` VM -> Click Settings ->
Network -> Adapter 1 -> Select 'Enable Network Adapter' ->
Select Attached to: Internal Network -> Select Name: intnet
* Configure the VirtualBox web service to disable authentication. (This is
only a suggestion. If you want, enable authentication with the appropriate
web service authentication library.)
::
VBoxManage setproperty websrvauthlibrary null
* Run VirtualBox web service::
C:\Program Files\Oracle\VirtualBox\VBoxWebSrv.exe
* Power on the ``cloud-controller`` VM.
* All the following instructions are to be done in the ``cloud-controller`` VM.
* Install the GNU/Linux distribution of your choice.
* Set up devstack.
* Install pyremotevbox::
sudo pip install "pyremotevbox>=0.5.0"
* Enable one (or more) of the VirtualBox drivers (``pxe_vbox``, ``agent_vbox``,
or ``fake_vbox``) via the ``enabled_drivers`` configuration option in
``/etc/ironic/ironic.conf``, and restart Ironic conductor.
* Set up flat networking on ``eth1``. For details on how to do this, see
`Configure Networking to communicate with the bare metal server`_.
* Enroll a VirtualBox node. The following examples use the ``pxe_vbox``
driver.
::
ironic node-create -d pxe_vbox -i virtualbox_host='10.0.2.2' -i virtualbox_vmname='baremetal'
If you are using authentication with VirtualBox web service, your username
and password need to be provided. The ironic node-create command will look
like::
ironic node-create -d pxe_vbox -i virtualbox_host='10.0.2.2' -i virtualbox_vmname='baremetal' -i virtualbox_username=<username> -i virtualbox_password=<password>
If VirtualBox web service is listening on a different port than the default
18083, then that port may be specified using the driver_info
parameter ``virtualbox_port``.
* Add other Node properties and trigger provisioning on the bare metal node.
.. note::
When a newly created bare metal VM is powered on for the first time by
Ironic (during provisioning), VirtualBox will automatically pop up a
dialog box asking to 'Select start-up disk'. Just press 'Cancel' to
continue booting the VM.
.. _`Configure Networking to communicate with the bare metal server`: http://docs.openstack.org/project-install-guide/baremetal/draft/configure-integration.html#configure-networking-to-communicate-with-the-bare-metal-server

View File

@ -10,14 +10,8 @@ pysnmp
python-ironic-inspector-client>=1.5.0
python-oneviewclient<3.0.0,>=2.5.2
python-scciclient>=0.4.0
python-seamicroclient>=0.4.0
UcsSdk==0.8.2.2
python-dracclient>=0.1.0
# '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
pyremotevbox>=0.5.0
# The CIMC drivers use the Cisco IMC SDK version 0.7.2 or greater
ImcSdk>=0.7.2

View File

@ -3291,20 +3291,6 @@
#ipxe_use_swift = false
[seamicro]
#
# From ironic
#
# Maximum retries for SeaMicro operations (integer value)
#max_retry = 3
# Seconds to wait for power action to be completed (integer
# value)
#action_timeout = 10
[service_catalog]
#
@ -3543,16 +3529,3 @@
# Username (string value)
# Deprecated group/name - [swift]/user-name
#username = <None>
[virtualbox]
#
# From ironic
#
# Port on which VirtualBox web service is listening. (port
# value)
# Minimum value: 0
# Maximum value: 65535
#port = 18083

View File

@ -43,8 +43,8 @@ service via hrefs.
There are however some limitations for different drivers:
* If you're using one of the drivers that use agent deploy method (namely,
``agent_ilo``, ``agent_ipmitool``, ``agent_pyghmi``, ``agent_ssh`` or
``agent_vbox``) you have to know MD5 checksum for your instance image. To
``agent_ilo``, ``agent_ipmitool``, ``agent_pyghmi`` or ``agent_ssh``)
you have to know MD5 checksum for your instance image. To
compute it, you can use the following command::
md5sum image.qcow2

View File

@ -481,10 +481,6 @@ class IPMIFailure(IronicException):
_msg_fmt = _("IPMI call failed: %(cmd)s.")
class MSFTOCSClientApiException(IronicException):
_msg_fmt = _("MSFT OCS call failed.")
class SSHConnectFailed(IronicException):
_msg_fmt = _("Failed to establish SSH connection to host %(host)s.")
@ -671,11 +667,6 @@ class IRMCSharedFileSystemNotMounted(IronicException):
_msg_fmt = _("iRMC shared file system '%(share)s' is not mounted.")
class VirtualBoxOperationFailed(IronicException):
_msg_fmt = _("VirtualBox operation '%(operation)s' failed. "
"Error: %(error)s")
class HardwareInspectionFailure(IronicException):
_msg_fmt = _("Failed to inspect hardware. Reason: %(error)s")

View File

@ -38,12 +38,10 @@ from ironic.conf import metrics_statsd
from ironic.conf import neutron
from ironic.conf import oneview
from ironic.conf import pxe
from ironic.conf import seamicro
from ironic.conf import service_catalog
from ironic.conf import snmp
from ironic.conf import ssh
from ironic.conf import swift
from ironic.conf import virtualbox
CONF = cfg.CONF
@ -70,9 +68,7 @@ metrics_statsd.register_opts(CONF)
neutron.register_opts(CONF)
oneview.register_opts(CONF)
pxe.register_opts(CONF)
seamicro.register_opts(CONF)
service_catalog.register_opts(CONF)
snmp.register_opts(CONF)
ssh.register_opts(CONF)
swift.register_opts(CONF)
virtualbox.register_opts(CONF)

View File

@ -54,12 +54,10 @@ _opts = [
('neutron', ironic.conf.neutron.list_opts()),
('oneview', ironic.conf.oneview.opts),
('pxe', ironic.conf.pxe.opts),
('seamicro', ironic.conf.seamicro.opts),
('service_catalog', ironic.conf.service_catalog.list_opts()),
('snmp', ironic.conf.snmp.opts),
('ssh', ironic.conf.ssh.opts),
('swift', ironic.conf.swift.list_opts()),
('virtualbox', ironic.conf.virtualbox.opts),
]

View File

@ -1,34 +0,0 @@
# Copyright 2016 Intel Corporation
#
# 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 SeaMicro operations')),
cfg.IntOpt('action_timeout',
default=10,
help=_('Seconds to wait for power action to be completed'))
]
opt_group = cfg.OptGroup(name='seamicro',
title='Options for the seamicro power driver')
def register_opts(conf):
conf.register_group(opt_group)
conf.register_opts(opts, group=opt_group)

View File

@ -1,27 +0,0 @@
# Copyright 2016 Intel Corporation
#
# 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.PortOpt('port',
default=18083,
help=_('Port on which VirtualBox web service is listening.')),
]
def register_opts(conf):
conf.register_opts(opts, group='virtualbox')

View File

@ -27,7 +27,6 @@ from ironic.drivers.modules import pxe
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
# For backward compatibility
@ -87,33 +86,6 @@ class AgentAndSSHDriver(base.BaseDriver):
self.console = ssh.ShellinaboxConsole()
class AgentAndVirtualBoxDriver(base.BaseDriver):
"""Agent + VirtualBox driver.
NOTE: This driver is meant only for testing environments.
This driver implements the `core` functionality, combining
:class:`ironic.drivers.modules.virtualbox.VirtualBoxPower` (for power
on/off and reboot of VirtualBox virtual machines), with
:class:`ironic.drivers.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('pyremotevbox'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
reason=_("Unable to import pyremotevbox library"))
self.power = virtualbox.VirtualBoxPower()
self.boot = pxe.PXEBoot()
self.deploy = agent.AgentDeploy()
self.management = virtualbox.VirtualBoxManagement()
self.raid = agent.AgentRAID()
class AgentAndUcsDriver(base.BaseDriver):
"""Agent + Cisco UCSM driver.

View File

@ -42,18 +42,14 @@ from ironic.drivers.modules.irmc import inspect as irmc_inspect
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers.modules.msftocs import management as msftocs_management
from ironic.drivers.modules.msftocs import power as msftocs_power
from ironic.drivers.modules.oneview import common as oneview_common
from ironic.drivers.modules.oneview import management as oneview_management
from ironic.drivers.modules.oneview import power as oneview_power
from ironic.drivers.modules import pxe
from ironic.drivers.modules import seamicro
from ironic.drivers.modules import snmp
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 import utils
@ -146,23 +142,6 @@ class FakeIPMINativeDriver(base.BaseDriver):
self.management = ipminative.NativeIPMIManagement()
class FakeSeaMicroDriver(base.BaseDriver):
"""Fake SeaMicro driver."""
supported = False
def __init__(self):
if not importutils.try_import('seamicroclient'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
reason=_("Unable to import seamicroclient library"))
self.power = seamicro.Power()
self.deploy = fake.FakeDeploy()
self.management = seamicro.Management()
self.vendor = seamicro.VendorPassthru()
self.console = seamicro.ShellinaboxConsole()
class FakeAgentDriver(base.BaseDriver):
"""Example implementation of an AgentDriver."""
@ -230,21 +209,6 @@ class FakeIRMCDriver(base.BaseDriver):
self.inspect = irmc_inspect.IRMCInspect()
class FakeVirtualBoxDriver(base.BaseDriver):
"""Fake VirtualBox driver."""
supported = False
def __init__(self):
if not importutils.try_import('pyremotevbox'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
reason=_("Unable to import pyremotevbox library"))
self.power = virtualbox.VirtualBoxPower()
self.deploy = fake.FakeDeploy()
self.management = virtualbox.VirtualBoxManagement()
class FakeIPMIToolInspectorDriver(base.BaseDriver):
"""Fake Inspector driver."""
@ -260,17 +224,6 @@ class FakeIPMIToolInspectorDriver(base.BaseDriver):
self.inspect = inspector.Inspector()
class FakeMSFTOCSDriver(base.BaseDriver):
"""Fake MSFT OCS driver."""
supported = False
def __init__(self):
self.power = msftocs_power.MSFTOCSPower()
self.deploy = fake.FakeDeploy()
self.management = msftocs_management.MSFTOCSManagement()
class FakeUcsDriver(base.BaseDriver):
"""Fake UCS driver."""

View File

@ -1,110 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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.
import copy
import re
import six
from ironic.common import exception
from ironic.common.i18n import _
from ironic.drivers.modules.msftocs import msftocsclient
REQUIRED_PROPERTIES = {
'msftocs_base_url': _('Base url of the OCS chassis manager REST API, '
'e.g.: http://10.0.0.1:8000. Required.'),
'msftocs_blade_id': _('Blade id, must be a number between 1 and the '
'maximum number of blades available in the chassis. '
'Required.'),
'msftocs_username': _('Username to access the chassis manager REST API. '
'Required.'),
'msftocs_password': _('Password to access the chassis manager REST API. '
'Required.'),
}
def get_client_info(driver_info):
"""Returns an instance of the REST API client and the blade id.
:param driver_info: the node's driver_info dict.
"""
client = msftocsclient.MSFTOCSClientApi(driver_info['msftocs_base_url'],
driver_info['msftocs_username'],
driver_info['msftocs_password'])
return client, driver_info['msftocs_blade_id']
def get_properties():
"""Returns the driver's properties."""
return copy.deepcopy(REQUIRED_PROPERTIES)
def _is_valid_url(url):
"""Checks whether a URL is valid.
:param url: a url string.
:returns: True if the url is valid or None, False otherwise.
"""
r = re.compile(
r'^https?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)*[A-Z]{2,6}\.?|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
return bool(isinstance(url, six.string_types) and r.search(url))
def _check_required_properties(driver_info):
"""Checks if all required properties are present.
:param driver_info: the node's driver_info dict.
:raises: MissingParameterValue if one or more required properties are
missing.
"""
missing_properties = set(REQUIRED_PROPERTIES) - set(driver_info)
if missing_properties:
raise exception.MissingParameterValue(
_('The following parameters were missing: %s') %
' '.join(missing_properties))
def parse_driver_info(node):
"""Checks for the required properties and values validity.
:param node: the target node.
:raises: MissingParameterValue if one or more required properties are
missing.
:raises: InvalidParameterValue if a parameter value is invalid.
"""
driver_info = node.driver_info
_check_required_properties(driver_info)
base_url = driver_info.get('msftocs_base_url')
if not _is_valid_url(base_url):
raise exception.InvalidParameterValue(
_('"%s" is not a valid "msftocs_base_url"') % base_url)
blade_id = driver_info.get('msftocs_blade_id')
try:
blade_id = int(blade_id)
except ValueError:
raise exception.InvalidParameterValue(
_('"%s" is not a valid "msftocs_blade_id"') % blade_id)
if blade_id < 1:
raise exception.InvalidParameterValue(
_('"msftocs_blade_id" must be greater than 0. The provided value '
'is: %s') % blade_id)

View File

@ -1,121 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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 ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers.modules.msftocs import common as msftocs_common
from ironic.drivers.modules.msftocs import msftocsclient
from ironic.drivers import utils as drivers_utils
BOOT_TYPE_TO_DEVICE_MAP = {
msftocsclient.BOOT_TYPE_FORCE_PXE: boot_devices.PXE,
msftocsclient.BOOT_TYPE_FORCE_DEFAULT_HDD: boot_devices.DISK,
msftocsclient.BOOT_TYPE_FORCE_INTO_BIOS_SETUP: boot_devices.BIOS,
}
DEVICE_TO_BOOT_TYPE_MAP = {v: k for k, v in BOOT_TYPE_TO_DEVICE_MAP.items()}
DEFAULT_BOOT_DEVICE = boot_devices.DISK
class MSFTOCSManagement(base.ManagementInterface):
def get_properties(self):
"""Returns the driver's properties."""
return msftocs_common.get_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 containing the target node.
:raises: MissingParameterValue if any required parameters are missing.
:raises: InvalidParameterValue if any parameters have invalid values.
"""
msftocs_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(BOOT_TYPE_TO_DEVICE_MAP.values())
def _check_valid_device(self, device, node):
"""Checks if the desired boot device is valid for this driver.
:param device: a boot device.
:param node: the target node.
:raises: InvalidParameterValue if the boot device is not valid.
"""
if device not in DEVICE_TO_BOOT_TYPE_MAP:
raise exception.InvalidParameterValue(
_("set_boot_device called with invalid device %(device)s for "
"node %(node_id)s.") %
{'device': device, 'node_id': node.uuid})
@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.
"""
self._check_valid_device(device, task.node)
client, blade_id = msftocs_common.get_client_info(
task.node.driver_info)
boot_mode = drivers_utils.get_node_capability(task.node, 'boot_mode')
uefi = (boot_mode == 'uefi')
boot_type = DEVICE_TO_BOOT_TYPE_MAP[device]
client.set_next_boot(blade_id, boot_type, persistent, uefi)
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.
"""
client, blade_id = msftocs_common.get_client_info(
task.node.driver_info)
device = BOOT_TYPE_TO_DEVICE_MAP.get(
client.get_next_boot(blade_id), DEFAULT_BOOT_DEVICE)
# Note(alexpilotti): Although the ChasssisManager REST API allows to
# specify the persistent boot status in SetNextBoot, currently it does
# not provide a way to retrieve the value with GetNextBoot.
# This is being addressed in the ChassisManager API.
return {'boot_device': device,
'persistent': None}
def get_sensors_data(self, task):
raise NotImplementedError()

View File

@ -1,176 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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.
"""
MSFT OCS ChassisManager v2.0 REST API client
https://github.com/MSOpenTech/ChassisManager
"""
import posixpath
from xml import etree
from oslo_log import log
import requests
from requests import auth
from requests import exceptions as requests_exceptions
from ironic.common import exception
from ironic.common.i18n import _, _LE
LOG = log.getLogger(__name__)
WCSNS = 'http://schemas.datacontract.org/2004/07/Microsoft.GFS.WCS.Contracts'
COMPLETION_CODE_SUCCESS = "Success"
BOOT_TYPE_UNKNOWN = 0
BOOT_TYPE_NO_OVERRIDE = 1
BOOT_TYPE_FORCE_PXE = 2
BOOT_TYPE_FORCE_DEFAULT_HDD = 3
BOOT_TYPE_FORCE_INTO_BIOS_SETUP = 4
BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE = 5
BOOT_TYPE_MAP = {
'Unknown': BOOT_TYPE_UNKNOWN,
'NoOverride': BOOT_TYPE_NO_OVERRIDE,
'ForcePxe': BOOT_TYPE_FORCE_PXE,
'ForceDefaultHdd': BOOT_TYPE_FORCE_DEFAULT_HDD,
'ForceIntoBiosSetup': BOOT_TYPE_FORCE_INTO_BIOS_SETUP,
'ForceFloppyOrRemovable': BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE,
}
POWER_STATUS_ON = "ON"
POWER_STATUS_OFF = "OFF"
class MSFTOCSClientApi(object):
def __init__(self, base_url, username, password):
self._base_url = base_url
self._username = username
self._password = password
def _exec_cmd(self, rel_url):
"""Executes a command by calling the chassis manager API."""
url = posixpath.join(self._base_url, rel_url)
try:
response = requests.get(
url, auth=auth.HTTPBasicAuth(self._username, self._password))
response.raise_for_status()
except requests_exceptions.RequestException as ex:
msg = _("HTTP call failed: %s") % ex
LOG.exception(msg)
raise exception.MSFTOCSClientApiException(msg)
xml_response = response.text
LOG.debug("Call to %(url)s got response: %(xml_response)s",
{"url": url, "xml_response": xml_response})
return xml_response
def _check_completion_code(self, xml_response):
try:
et = etree.ElementTree.fromstring(xml_response)
except etree.ElementTree.ParseError as ex:
LOG.exception(_LE("XML parsing failed: %s"), ex)
raise exception.MSFTOCSClientApiException(
_("Invalid XML: %s") % xml_response)
item = et.find("./n:completionCode", namespaces={'n': WCSNS})
if item is None or item.text != COMPLETION_CODE_SUCCESS:
raise exception.MSFTOCSClientApiException(
_("Operation failed: %s") % xml_response)
return et
def get_blade_state(self, blade_id):
"""Returns whether a blade's chipset is receiving power (soft-power).
:param blade_id: the blade id
:returns: one of:
POWER_STATUS_ON,
POWER_STATUS_OFF
:raises: MSFTOCSClientApiException
"""
et = self._check_completion_code(
self._exec_cmd("GetBladeState?bladeId=%d" % blade_id))
return et.find('./n:bladeState', namespaces={'n': WCSNS}).text
def set_blade_on(self, blade_id):
"""Supplies power to a blade chipset (soft-power state).
:param blade_id: the blade id
:raises: MSFTOCSClientApiException
"""
self._check_completion_code(
self._exec_cmd("SetBladeOn?bladeId=%d" % blade_id))
def set_blade_off(self, blade_id):
"""Shuts down a given blade (soft-power state).
:param blade_id: the blade id
:raises: MSFTOCSClientApiException
"""
self._check_completion_code(
self._exec_cmd("SetBladeOff?bladeId=%d" % blade_id))
def set_blade_power_cycle(self, blade_id, off_time=0):
"""Performs a soft reboot of a given blade.
:param blade_id: the blade id
:param off_time: seconds to wait between shutdown and boot
:raises: MSFTOCSClientApiException
"""
self._check_completion_code(
self._exec_cmd("SetBladeActivePowerCycle?bladeId=%(blade_id)d&"
"offTime=%(off_time)d" %
{"blade_id": blade_id, "off_time": off_time}))
def get_next_boot(self, blade_id):
"""Returns the next boot device configured for a given blade.
:param blade_id: the blade id
:returns: one of:
BOOT_TYPE_UNKNOWN,
BOOT_TYPE_NO_OVERRIDE,
BOOT_TYPE_FORCE_PXE, BOOT_TYPE_FORCE_DEFAULT_HDD,
BOOT_TYPE_FORCE_INTO_BIOS_SETUP,
BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE
:raises: MSFTOCSClientApiException
"""
et = self._check_completion_code(
self._exec_cmd("GetNextBoot?bladeId=%d" % blade_id))
return BOOT_TYPE_MAP[
et.find('./n:nextBoot', namespaces={'n': WCSNS}).text]
def set_next_boot(self, blade_id, boot_type, persistent=True, uefi=True):
"""Sets the next boot device for a given blade.
:param blade_id: the blade id
:param boot_type: possible values:
BOOT_TYPE_UNKNOWN,
BOOT_TYPE_NO_OVERRIDE,
BOOT_TYPE_FORCE_PXE,
BOOT_TYPE_FORCE_DEFAULT_HDD,
BOOT_TYPE_FORCE_INTO_BIOS_SETUP,
BOOT_TYPE_FORCE_FLOPPY_OR_REMOVABLE
:param persistent: whether this setting affects the next boot only or
every subsequent boot
:param uefi: True if UEFI, False otherwise
:raises: MSFTOCSClientApiException
"""
self._check_completion_code(
self._exec_cmd(
"SetNextBoot?bladeId=%(blade_id)d&bootType=%(boot_type)d&"
"uefi=%(uefi)s&persistent=%(persistent)s" %
{"blade_id": blade_id,
"boot_type": boot_type,
"uefi": str(uefi).lower(),
"persistent": str(persistent).lower()}))

View File

@ -1,105 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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.
"""
MSFT OCS Power Driver
"""
from oslo_log import log
from ironic.common import exception
from ironic.common.i18n import _, _LE
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers.modules.msftocs import common as msftocs_common
from ironic.drivers.modules.msftocs import msftocsclient
LOG = log.getLogger(__name__)
POWER_STATES_MAP = {
msftocsclient.POWER_STATUS_ON: states.POWER_ON,
msftocsclient.POWER_STATUS_OFF: states.POWER_OFF,
}
class MSFTOCSPower(base.PowerInterface):
def get_properties(self):
"""Returns the driver's properties."""
return msftocs_common.get_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 containing the target node.
:raises: MissingParameterValue if any required parameters are missing.
:raises: InvalidParameterValue if any parameters have invalid values.
"""
msftocs_common.parse_driver_info(task.node)
def get_power_state(self, task):
"""Get the power state from the node.
:param task: a TaskManager instance containing the target node.
:raises: MSFTOCSClientApiException.
"""
client, blade_id = msftocs_common.get_client_info(
task.node.driver_info)
return POWER_STATES_MAP[client.get_blade_state(blade_id)]
@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: InvalidParameterValue
"""
client, blade_id = msftocs_common.get_client_info(
task.node.driver_info)
try:
if pstate == states.POWER_ON:
client.set_blade_on(blade_id)
elif pstate == states.POWER_OFF:
client.set_blade_off(blade_id)
else:
raise exception.InvalidParameterValue(
_('Unsupported target_state: %s') % pstate)
except exception.MSFTOCSClientApiException as ex:
LOG.exception(_LE("Changing the power state to %(pstate)s failed. "
"Error: %(err_msg)s"),
{"pstate": pstate, "err_msg": ex})
raise exception.PowerStateFailure(pstate=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.
"""
client, blade_id = msftocs_common.get_client_info(
task.node.driver_info)
try:
client.set_blade_power_cycle(blade_id)
except exception.MSFTOCSClientApiException as ex:
LOG.exception(_LE("Reboot failed. Error: %(err_msg)s"),
{"err_msg": ex})
raise exception.PowerStateFailure(pstate=states.REBOOT)

View File

@ -1,672 +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.
"""
Ironic SeaMicro interfaces.
Provides basic power control of servers in SeaMicro chassis via
python-seamicroclient.
Provides vendor passthru methods for SeaMicro specific functionality.
"""
import os
import re
from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import importutils
from six.moves.urllib import parse as urlparse
from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _, _LE, _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
from ironic.drivers.modules import console_utils
seamicroclient = importutils.try_import('seamicroclient')
if seamicroclient:
from seamicroclient import client as seamicro_client
from seamicroclient import exceptions as seamicro_client_exception
LOG = logging.getLogger(__name__)
_BOOT_DEVICES_MAP = {
boot_devices.DISK: 'hd0',
boot_devices.PXE: 'pxe',
}
REQUIRED_PROPERTIES = {
'seamicro_api_endpoint': _("API endpoint. Required."),
'seamicro_password': _("password. Required."),
'seamicro_server_id': _("server ID. Required."),
'seamicro_username': _("username. Required."),
}
OPTIONAL_PROPERTIES = {
'seamicro_api_version': _("version of SeaMicro API client; default is 2. "
"Optional.")
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
CONSOLE_PROPERTIES = {
'seamicro_terminal_port': _("node's UDP port to connect to. "
"Only required for console access.")
}
PORT_BASE = 2000
def _get_client(*args, **kwargs):
"""Creates the python-seamicro_client
:param kwargs: A dict of keyword arguments to be passed to the method,
which should contain: 'username', 'password',
'auth_url', 'api_version' parameters.
:returns: SeaMicro API client.
"""
cl_kwargs = {'username': kwargs['username'],
'password': kwargs['password'],
'auth_url': kwargs['api_endpoint']}
try:
return seamicro_client.Client(kwargs['api_version'], **cl_kwargs)
except seamicro_client_exception.UnsupportedVersion as e:
raise exception.InvalidParameterValue(_(
"Invalid 'seamicro_api_version' parameter. Reason: %s.") % e)
def _parse_driver_info(node):
"""Parses and creates seamicro driver info
:param node: An Ironic node object.
:returns: SeaMicro driver info.
:raises: MissingParameterValue if any required parameters are missing.
:raises: InvalidParameterValue if required parameter are invalid.
"""
info = node.driver_info or {}
missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
if missing_info:
raise exception.MissingParameterValue(_(
"SeaMicro driver requires the following parameters to be set in"
" node's driver_info: %s.") % missing_info)
api_endpoint = info.get('seamicro_api_endpoint')
username = info.get('seamicro_username')
password = info.get('seamicro_password')
server_id = info.get('seamicro_server_id')
api_version = info.get('seamicro_api_version', "2")
port = info.get('seamicro_terminal_port')
if port is not None:
port = utils.validate_network_port(port, 'seamicro_terminal_port')
r = re.compile(r"(^[0-9]+)/([0-9]+$)")
if not r.match(server_id):
raise exception.InvalidParameterValue(_(
"Invalid 'seamicro_server_id' parameter in node's "
"driver_info. Expected format of 'seamicro_server_id' "
"is <int>/<int>"))
url = urlparse.urlparse(api_endpoint)
if (not (url.scheme == "http") or not url.netloc):
raise exception.InvalidParameterValue(_(
"Invalid 'seamicro_api_endpoint' parameter in node's "
"driver_info."))
res = {'username': username,
'password': password,
'api_endpoint': api_endpoint,
'server_id': server_id,
'api_version': api_version,
'uuid': node.uuid,
'port': port}
return res
def _get_server(driver_info):
"""Get server from server_id."""
s_client = _get_client(**driver_info)
return s_client.servers.get(driver_info['server_id'])
def _get_volume(driver_info, volume_id):
"""Get volume from volume_id."""
s_client = _get_client(**driver_info)
return s_client.volumes.get(volume_id)
def _get_power_status(node):
"""Get current power state of this node
:param node: Ironic node one of :class:`ironic.db.models.Node`
:raises: InvalidParameterValue if a seamicro parameter is invalid.
:raises: MissingParameterValue if required seamicro parameters are
missing.
:raises: ServiceUnavailable on an error from SeaMicro Client.
:returns: Power state of the given node
"""
seamicro_info = _parse_driver_info(node)
try:
server = _get_server(seamicro_info)
if not hasattr(server, 'active') or server.active is None:
return states.ERROR
if not server.active:
return states.POWER_OFF
elif server.active:
return states.POWER_ON
except seamicro_client_exception.NotFound:
raise exception.NodeNotFound(node=node.uuid)
except seamicro_client_exception.ClientException as ex:
LOG.error(_LE("SeaMicro client exception %(msg)s for node %(uuid)s"),
{'msg': ex.message, 'uuid': node.uuid})
raise exception.ServiceUnavailable(message=ex.message)
def _power_on(node, timeout=None):
"""Power ON this node
:param node: An Ironic node object.
:param timeout: Time in seconds to wait till power on is complete.
:raises: InvalidParameterValue if a seamicro parameter is invalid.
:raises: MissingParameterValue if required seamicro parameters are
missing.
:returns: Power state of the given node.
"""
if timeout is None:
timeout = CONF.seamicro.action_timeout
state = [None]
retries = [0]
seamicro_info = _parse_driver_info(node)
server = _get_server(seamicro_info)
def _wait_for_power_on(state, retries):
"""Called at an interval until the node is powered on."""
state[0] = _get_power_status(node)
if state[0] == states.POWER_ON:
raise loopingcall.LoopingCallDone()
if retries[0] > CONF.seamicro.max_retry:
state[0] = states.ERROR
raise loopingcall.LoopingCallDone()
try:
retries[0] += 1
server.power_on()
except seamicro_client_exception.ClientException:
LOG.warning(_LW("Power-on failed for node %s."),
node.uuid)
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_on,
state, retries)
timer.start(interval=timeout).wait()
return state[0]
def _power_off(node, timeout=None):
"""Power OFF this node
:param node: Ironic node one of :class:`ironic.db.models.Node`
:param timeout: Time in seconds to wait till power off is compelete
:raises: InvalidParameterValue if a seamicro parameter is invalid.
:raises: MissingParameterValue if required seamicro parameters are
missing.
:returns: Power state of the given node
"""
if timeout is None:
timeout = CONF.seamicro.action_timeout
state = [None]
retries = [0]
seamicro_info = _parse_driver_info(node)
server = _get_server(seamicro_info)
def _wait_for_power_off(state, retries):
"""Called at an interval until the node is powered off."""
state[0] = _get_power_status(node)
if state[0] == states.POWER_OFF:
raise loopingcall.LoopingCallDone()
if retries[0] > CONF.seamicro.max_retry:
state[0] = states.ERROR
raise loopingcall.LoopingCallDone()
try:
retries[0] += 1
server.power_off()
except seamicro_client_exception.ClientException:
LOG.warning(_LW("Power-off failed for node %s."),
node.uuid)
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_off,
state, retries)
timer.start(interval=timeout).wait()
return state[0]
def _reboot(node, timeout=None):
"""Reboot this node.
:param node: Ironic node one of :class:`ironic.db.models.Node`
:param timeout: Time in seconds to wait till reboot is compelete
:raises: InvalidParameterValue if a seamicro parameter is invalid.
:raises: MissingParameterValue if required seamicro parameters are
missing.
:returns: Power state of the given node
"""
if timeout is None:
timeout = CONF.seamicro.action_timeout
state = [None]
retries = [0]
seamicro_info = _parse_driver_info(node)
server = _get_server(seamicro_info)
def _wait_for_reboot(state, retries):
"""Called at an interval until the node is rebooted successfully."""
state[0] = _get_power_status(node)
if state[0] == states.POWER_ON:
raise loopingcall.LoopingCallDone()
if retries[0] > CONF.seamicro.max_retry:
state[0] = states.ERROR
raise loopingcall.LoopingCallDone()
try:
retries[0] += 1
server.reset()
except seamicro_client_exception.ClientException:
LOG.warning(_LW("Reboot failed for node %s."),
node.uuid)
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot,
state, retries)
server.reset()
timer.start(interval=timeout).wait()
return state[0]
def _validate_volume(driver_info, volume_id):
"""Validates if volume is in Storage pools designated for ironic."""
volume = _get_volume(driver_info, volume_id)
# Check if the ironic <scard>/ironic-<pool_id>/<volume_id> naming scheme
# is present in volume id
try:
pool_id = volume.id.split('/')[1].lower()
except IndexError:
pool_id = ""
if "ironic-" in pool_id:
return True
else:
raise exception.InvalidParameterValue(_(
"Invalid volume id specified"))
def _get_pools(driver_info, filters=None):
"""Get SeaMicro storage pools matching given filters."""
s_client = _get_client(**driver_info)
return s_client.pools.list(filters=filters)
def _create_volume(driver_info, volume_size):
"""Create volume in the SeaMicro storage pools designated for ironic."""
ironic_pools = _get_pools(driver_info, filters={'id': 'ironic-'})
if ironic_pools is None:
raise exception.VendorPassthruException(_(
"No storage pools found for ironic"))
least_used_pool = sorted(ironic_pools,
key=lambda x: x.freeSize)[0]
return _get_client(**driver_info).volumes.create(volume_size,
least_used_pool)
def get_telnet_port(driver_info):
"""Get SeaMicro telnet port to listen."""
server_id = int(driver_info['server_id'].split("/")[0])
return PORT_BASE + (10 * server_id)
class Power(base.PowerInterface):
"""SeaMicro Power Interface.
This PowerInterface class provides a mechanism for controlling the power
state of servers in a seamicro chassis.
"""
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task):
"""Check that node 'driver_info' is valid.
Check that node 'driver_info' contains the required fields.
:param task: a TaskManager instance containing the node to act on.
:raises: MissingParameterValue if required seamicro parameters are
missing.
"""
_parse_driver_info(task.node)
def get_power_state(self, task):
"""Get the current power state of the task's node.
Poll the host for the current power state of the node.
:param task: a TaskManager instance containing the node to act on.
:raises: ServiceUnavailable on an error from SeaMicro Client.
:raises: InvalidParameterValue if a seamicro parameter is invalid.
:raises: MissingParameterValue when a required parameter is missing
:returns: power state. One of :class:`ironic.common.states`.
"""
return _get_power_status(task.node)
@task_manager.require_exclusive_lock
def set_power_state(self, task, pstate):
"""Turn the power on or off.
Set the power state of a node.
:param task: a TaskManager instance containing the node to act on.
:param pstate: Either POWER_ON or POWER_OFF from :class:
`ironic.common.states`.
:raises: InvalidParameterValue if an invalid power state was specified
or a seamicro parameter is invalid.
:raises: MissingParameterValue when a required parameter is missing
:raises: PowerStateFailure if the desired power state couldn't be set.
"""
if pstate == states.POWER_ON:
state = _power_on(task.node)
elif pstate == states.POWER_OFF:
state = _power_off(task.node)
else:
raise exception.InvalidParameterValue(_(
"set_power_state called with invalid power state."))
if state != pstate:
raise exception.PowerStateFailure(pstate=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 a seamicro parameter is invalid.
:raises: MissingParameterValue if required seamicro parameters are
missing.
:raises: PowerStateFailure if the final state of the node is not
POWER_ON.
"""
state = _reboot(task.node)
if state != states.POWER_ON:
raise exception.PowerStateFailure(pstate=states.POWER_ON)
class VendorPassthru(base.VendorInterface):
"""SeaMicro vendor-specific methods."""
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task, method, **kwargs):
_parse_driver_info(task.node)
@base.passthru(['POST'],
description=_("Set an untagged VLAN ID for NIC 0 of node. "
"Required argument: 'vlan_id' - ID of "
"untagged VLAN."))
def set_node_vlan_id(self, task, **kwargs):
"""Sets an untagged vlan id for NIC 0 of node.
@kwargs vlan_id: id of untagged vlan for NIC 0 of node
"""
node = task.node
vlan_id = kwargs.get('vlan_id')
if not vlan_id:
raise exception.MissingParameterValue(_("No vlan id provided"))
seamicro_info = _parse_driver_info(node)
try:
server = _get_server(seamicro_info)
# remove current vlan for server
if len(server.nic['0']['untaggedVlan']) > 0:
server.unset_untagged_vlan(server.nic['0']['untaggedVlan'])
server = server.refresh(5)
server.set_untagged_vlan(vlan_id)
except seamicro_client_exception.ClientException as ex:
LOG.error(_LE("SeaMicro client exception: %s"), ex.message)
raise exception.VendorPassthruException(message=ex.message)
properties = node.properties
properties['seamicro_vlan_id'] = vlan_id
node.properties = properties
node.save()
@base.passthru(['POST'],
description=_("Attach volume to node. Arguments: "
"1. 'volume_id' - ID of pre-provisioned "
"volume. This is optional. If not specified, "
"a volume is created in SeaMicro storage "
"pool. 2. 'volume_size' - size of new volume "
"(if volume_id is not specified)."))
def attach_volume(self, task, **kwargs):
"""Attach a volume to a node.
Attach volume from SeaMicro storage pools for ironic to node.
If kwargs['volume_id'] not given, Create volume in SeaMicro
storage pool and attach to node.
@kwargs volume_id: id of pre-provisioned volume that is to be attached
as root volume of node
@kwargs volume_size: size of new volume to be created and attached
as root volume of node
"""
node = task.node
seamicro_info = _parse_driver_info(node)
volume_id = kwargs.get('volume_id')
if volume_id is None:
volume_size = kwargs.get('volume_size')
if volume_size is None:
raise exception.MissingParameterValue(
_("No volume size provided for creating volume"))
volume_id = _create_volume(seamicro_info, volume_size)
if _validate_volume(seamicro_info, volume_id):
try:
server = _get_server(seamicro_info)
server.detach_volume()
server = server.refresh(5)
server.attach_volume(volume_id)
except seamicro_client_exception.ClientException as ex:
LOG.error(_LE("SeaMicro client exception: %s"), ex.message)
raise exception.VendorPassthruException(message=ex.message)
properties = node.properties
properties['seamicro_volume_id'] = volume_id
node.properties = properties
node.save()
class Management(base.ManagementInterface):
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task):
"""Check that 'driver_info' contains SeaMicro credentials.
Validates whether the 'driver_info' property of the supplied
task's node contains the required credentials information.
:param task: a task from TaskManager.
:raises: MissingParameterValue when a required parameter is missing
"""
_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 defined
in :mod:`ironic.common.boot_devices`.
"""
return list(_BOOT_DEVICES_MAP.keys())
@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 reboot of the node.
:param task: a task from TaskManager.
:param device: the boot device, one of
:mod:`ironic.common.boot_devices`.
:param persistent: Boolean value. True if the boot device will
persist to all future boots, False if not.
Default: False. Ignored by this driver.
:raises: InvalidParameterValue if an invalid boot device is
specified or if a seamicro parameter is invalid.
:raises: IronicException on an error from seamicro-client.
:raises: MissingParameterValue when a required parameter is missing
"""
if device not in self.get_supported_boot_devices(task):
raise exception.InvalidParameterValue(_(
"Invalid boot device %s specified.") % device)
seamicro_info = _parse_driver_info(task.node)
try:
server = _get_server(seamicro_info)
boot_device = _BOOT_DEVICES_MAP[device]
server.set_boot_order(boot_device)
except seamicro_client_exception.ClientException as ex:
LOG.error(_LE("Seamicro set boot device failed for node "
"%(node)s with the following error: %(error)s"),
{'node': task.node.uuid, 'error': ex})
raise exception.IronicException(ex)
def get_boot_device(self, task):
"""Get the current boot device for the task's node.
Returns the current boot device of the node. Be aware that not
all drivers support this.
:param task: a task from TaskManager.
:returns: a dictionary containing:
:boot_device: the boot device, one of
:mod:`ironic.common.boot_devices` or None if it is unknown.
:persistent: Whether the boot device will persist to all
future boots or not, None if it is unknown.
"""
# TODO(lucasagomes): The python-seamicroclient library currently
# doesn't expose a method to get the boot device, update it once
# it's implemented.
return {'boot_device': None, 'persistent': None}
def get_sensors_data(self, task):
"""Get sensors data method.
Not implemented by this driver.
:param task: a TaskManager instance.
"""
raise NotImplementedError()
class ShellinaboxConsole(base.ConsoleInterface):
"""A ConsoleInterface that uses telnet and shellinabox."""
def get_properties(self):
d = COMMON_PROPERTIES.copy()
d.update(CONSOLE_PROPERTIES)
return d
def validate(self, task):
"""Validate the Node console info.
:param task: a task from TaskManager.
:raises: MissingParameterValue if required seamicro parameters are
missing
:raises: InvalidParameterValue if required parameter are invalid.
"""
driver_info = _parse_driver_info(task.node)
if not driver_info['port']:
raise exception.MissingParameterValue(_(
"Missing 'seamicro_terminal_port' parameter in node's "
"driver_info"))
def start_console(self, task):
"""Start a remote console for the node.
:param task: a task from TaskManager
:raises: MissingParameterValue if required seamicro parameters are
missing
:raises: ConsoleError if the directory for the PID file cannot be
created
:raises: ConsoleSubprocessFailed when invoking the subprocess failed
:raises: InvalidParameterValue if required parameter are invalid.
"""
driver_info = _parse_driver_info(task.node)
telnet_port = get_telnet_port(driver_info)
chassis_ip = urlparse.urlparse(driver_info['api_endpoint']).netloc
seamicro_cmd = ("/:%(uid)s:%(gid)s:HOME:telnet %(chassis)s %(port)s"
% {'uid': os.getuid(),
'gid': os.getgid(),
'chassis': chassis_ip,
'port': telnet_port})
console_utils.start_shellinabox_console(driver_info['uuid'],
driver_info['port'],
seamicro_cmd)
def stop_console(self, task):
"""Stop the remote console session for the node.
:param task: a task from TaskManager
:raises: ConsoleError if unable to stop the console
"""
console_utils.stop_shellinabox_console(task.node.uuid)
def get_console(self, task):
"""Get the type and connection information about the console.
:raises: MissingParameterValue if required seamicro parameters are
missing
:raises: InvalidParameterValue if required parameter are invalid.
"""
driver_info = _parse_driver_info(task.node)
url = console_utils.get_shellinabox_console_url(driver_info['port'])
return {'type': 'shellinabox', 'url': url}

View File

@ -1,393 +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.
"""
VirtualBox Driver Modules
"""
from oslo_log import log as logging
from oslo_utils import importutils
from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _, _LE, _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
pyremotevbox = importutils.try_import('pyremotevbox')
if pyremotevbox:
from pyremotevbox import exception as virtualbox_exc
from pyremotevbox import vbox as virtualbox
IRONIC_TO_VIRTUALBOX_DEVICE_MAPPING = {
boot_devices.PXE: 'Network',
boot_devices.DISK: 'HardDisk',
boot_devices.CDROM: 'DVD',
}
VIRTUALBOX_TO_IRONIC_DEVICE_MAPPING = {
v: k for k, v in IRONIC_TO_VIRTUALBOX_DEVICE_MAPPING.items()}
VIRTUALBOX_TO_IRONIC_POWER_MAPPING = {
'PoweredOff': states.POWER_OFF,
'Running': states.POWER_ON,
'Error': states.ERROR
}
LOG = logging.getLogger(__name__)
REQUIRED_PROPERTIES = {
'virtualbox_vmname': _("Name of the VM in VirtualBox. Required."),
'virtualbox_host': _("IP address or hostname of the VirtualBox host. "
"Required.")
}
OPTIONAL_PROPERTIES = {
'virtualbox_username': _("Username for the VirtualBox host. "
"Default value is ''. Optional."),
'virtualbox_password': _("Password for 'virtualbox_username'. "
"Default value is ''. Optional."),
'virtualbox_port': _("Port on which VirtualBox web service is listening. "
"Optional."),
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
def _strip_virtualbox_from_param_name(param_name):
if param_name.startswith('virtualbox_'):
return param_name[11:]
else:
return param_name
def _parse_driver_info(node):
"""Gets the driver specific node driver info.
This method validates whether the 'driver_info' property of the
supplied node contains the required information for this driver.
:param node: an Ironic Node object.
:returns: a dict containing information from driver_info (or where
applicable, config values).
:raises: MissingParameterValue, if some required parameter(s) are missing
in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid value(s)
in the node's driver_info.
"""
info = node.driver_info
d_info = {}
missing_params = []
for param in REQUIRED_PROPERTIES:
try:
d_info_param_name = _strip_virtualbox_from_param_name(param)
d_info[d_info_param_name] = info[param]
except KeyError:
missing_params.append(param)
if missing_params:
msg = (_("The following parameters are missing in driver_info: %s") %
', '.join(missing_params))
raise exception.MissingParameterValue(msg)
for param in OPTIONAL_PROPERTIES:
if param in info:
d_info_param_name = _strip_virtualbox_from_param_name(param)
d_info[d_info_param_name] = info[param]
port = d_info.get('port', CONF.virtualbox.port)
d_info['port'] = utils.validate_network_port(port, 'virtualbox_port')
return d_info
def _run_virtualbox_method(node, ironic_method, vm_object_method,
*call_args, **call_kwargs):
"""Runs a method of pyremotevbox.vbox.VirtualMachine
This runs a method from pyremotevbox.vbox.VirtualMachine.
The VirtualMachine method to be invoked and the argument(s) to be
passed to it are to be provided.
:param node: an Ironic Node object.
:param ironic_method: the Ironic method which called
'_run_virtualbox_method'. This is used for logging only.
:param vm_object_method: The method on the VirtualMachine object
to be called.
:param call_args: The args to be passed to 'vm_object_method'.
:param call_kwargs: The kwargs to be passed to the 'vm_object_method'.
:returns: The value returned by 'vm_object_method'
:raises: VirtualBoxOperationFailed, if execution of 'vm_object_method'
failed.
:raises: InvalidParameterValue,
- if 'vm_object_method' is not a valid 'VirtualMachine' method.
- if some parameter(s) have invalid value(s) in the node's driver_info.
:raises: MissingParameterValue, if some required parameter(s) are missing
in the node's driver_info.
:raises: pyremotevbox.exception.VmInWrongPowerState, if operation cannot
be performed when vm is in the current power state.
"""
driver_info = _parse_driver_info(node)
try:
host = virtualbox.VirtualBoxHost(**driver_info)
vm_object = host.find_vm(driver_info['vmname'])
except virtualbox_exc.PyRemoteVBoxException as exc:
LOG.error(_LE("Failed while creating a VirtualMachine object for "
"node %(node_id)s. Error: %(error)s."),
{'node_id': node.uuid, 'error': exc})
raise exception.VirtualBoxOperationFailed(operation=vm_object_method,
error=exc)
try:
func = getattr(vm_object, vm_object_method)
except AttributeError:
error_msg = _("Invalid VirtualMachine method '%s' passed "
"to '_run_virtualbox_method'.")
raise exception.InvalidParameterValue(error_msg % vm_object_method)
try:
return func(*call_args, **call_kwargs)
except virtualbox_exc.PyRemoteVBoxException as exc:
error_msg = _LE("'%(ironic_method)s' failed for node %(node_id)s with "
"error: %(error)s.")
LOG.error(error_msg, {'ironic_method': ironic_method,
'node_id': node.uuid,
'error': exc})
raise exception.VirtualBoxOperationFailed(operation=vm_object_method,
error=exc)
class VirtualBoxPower(base.PowerInterface):
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task):
"""Check if node.driver_info contains the required credentials.
:param task: a TaskManager instance.
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
"""
_parse_driver_info(task.node)
def _apply_boot_device(self, task):
"""Get the target boot device and apply on the baremetal machine .
:param task: a TaskManager instance.
"""
driver_internal_info = task.node.driver_internal_info
device = driver_internal_info.pop('vbox_target_boot_device', None)
if device is not None:
task.driver.management.set_boot_device(task, device)
task.node.driver_internal_info = driver_internal_info
task.node.save()
def get_power_state(self, task):
"""Gets the current power state.
:param task: a TaskManager instance.
:returns: one of :mod:`ironic.common.states`
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
:raises: VirtualBoxOperationFailed, if error encountered from
VirtualBox operation.
"""
power_status = _run_virtualbox_method(task.node, 'get_power_state',
'get_power_status')
try:
return VIRTUALBOX_TO_IRONIC_POWER_MAPPING[power_status]
except KeyError:
msg = _LE("VirtualBox returned unknown state '%(state)s' for "
"node %(node)s")
LOG.error(msg, {'state': power_status, 'node': task.node.uuid})
return states.ERROR
@task_manager.require_exclusive_lock
def set_power_state(self, task, target_state):
"""Turn the current power state on or off.
:param task: a TaskManager instance.
:param target_state: The desired power state POWER_ON,POWER_OFF or
REBOOT from :mod:`ironic.common.states`.
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info OR if an invalid power state
was specified.
:raises: VirtualBoxOperationFailed, if error encountered from
VirtualBox operation.
"""
# We set boot device before power on to avoid the case that user
# shuts down the machine without calling power off method here. For
# instance, soft power off the machine from OS.
if target_state == states.POWER_OFF:
_run_virtualbox_method(task.node, 'set_power_state', 'stop')
self._apply_boot_device(task)
elif target_state == states.POWER_ON:
self._apply_boot_device(task)
_run_virtualbox_method(task.node, 'set_power_state', 'start')
elif target_state == states.REBOOT:
self.reboot(task)
else:
msg = _("'set_power_state' called with invalid power "
"state '%s'") % target_state
raise exception.InvalidParameterValue(msg)
@task_manager.require_exclusive_lock
def reboot(self, task):
"""Reboot the node.
:param task: a TaskManager instance.
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
:raises: VirtualBoxOperationFailed, if error encountered from
VirtualBox operation.
"""
_run_virtualbox_method(task.node, 'reboot', 'stop')
self._apply_boot_device(task)
_run_virtualbox_method(task.node, 'reboot', 'start')
class VirtualBoxManagement(base.ManagementInterface):
def get_properties(self):
return COMMON_PROPERTIES
def validate(self, task):
"""Check that 'driver_info' contains required credentials.
Validates whether the 'driver_info' property of the supplied
task's node contains the required credentials information.
:param task: a task from TaskManager.
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
"""
_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 defined
in :mod:`ironic.common.boot_devices`.
"""
return list(IRONIC_TO_VIRTUALBOX_DEVICE_MAPPING.keys())
def _get_boot_device_from_hardware(self, task):
boot_dev = _run_virtualbox_method(task.node,
'get_boot_device', 'get_boot_device')
ironic_boot_dev = VIRTUALBOX_TO_IRONIC_DEVICE_MAPPING.get(boot_dev)
persistent = True
if not ironic_boot_dev:
persistent = None
msg = _LW("VirtualBox returned unknown boot "
"device '%(device)s' for node %(node)s")
LOG.warning(msg, {'device': boot_dev, 'node': task.node.uuid})
return (ironic_boot_dev, persistent)
def get_boot_device(self, task):
"""Get the current boot device for a node.
:param task: a task from TaskManager.
:returns: a dictionary containing:
'boot_device': one of the ironic.common.boot_devices or None
'persistent': True if boot device is persistent, False otherwise
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
:raises: VirtualBoxOperationFailed, if error encountered from
VirtualBox operation.
"""
if task.driver.power.get_power_state(task) == states.POWER_OFF:
ironic_boot_dev, persistent = \
self._get_boot_device_from_hardware(task)
else:
ironic_boot_dev = task.node. \
driver_internal_info.get('vbox_target_boot_device')
if ironic_boot_dev is not None:
msg = _LW("As ironic node %(node)s is"
" powered on, we will set to boot"
" from %(device)s before next boot.")
LOG.warning(msg, {'node': task.node.uuid,
'device': ironic_boot_dev})
persistent = True
else:
# Maybe the vbox_target_boot_device is cleaned
ironic_boot_dev, persistent = \
self._get_boot_device_from_hardware(task)
return {'boot_device': ironic_boot_dev, 'persistent': persistent}
@task_manager.require_exclusive_lock
def set_boot_device(self, task, device, persistent=False):
"""Set the boot device for a node.
:param task: a task from TaskManager.
:param device: ironic.common.boot_devices
:param persistent: This argument is ignored as VirtualBox support only
persistent boot devices.
:raises: MissingParameterValue, if some required parameter(s) are
missing in the node's driver_info.
:raises: InvalidParameterValue, if some parameter(s) have invalid
value(s) in the node's driver_info.
:raises: VirtualBoxOperationFailed, if error encountered from
VirtualBox operation.
"""
# NOTE(rameshg87): VirtualBox has only persistent boot devices.
try:
boot_dev = IRONIC_TO_VIRTUALBOX_DEVICE_MAPPING[device]
except KeyError:
raise exception.InvalidParameterValue(
_("Invalid boot device %s specified.") % device)
if task.driver.power.get_power_state(task) == states.POWER_OFF:
_run_virtualbox_method(task.node, 'set_boot_device',
'set_boot_device', boot_dev)
else:
LOG.warning(_LW('Node %(node_uuid)s: As VirtualBox do not support '
'setting boot device when VM is powered on, we '
'will set booting from %(device)s when reboot '
'next time.'),
{'node_uuid': task.node.uuid, 'device': device})
# We should store target boot device in case the
# end user shutoff the baremetal machine from NOVA API.
boot_device_now = self.get_boot_device(task)['boot_device']
if device != boot_device_now:
driver_internal_info = task.node.driver_internal_info
driver_internal_info['vbox_target_boot_device'] = device
task.node.driver_internal_info = driver_internal_info
task.node.save()
def get_sensors_data(self, task):
"""Get sensors data.
:param task: a TaskManager instance.
:raises: FailedToGetSensorData when getting the sensor data fails.
:raises: FailedToParseSensorData when parsing sensor data fails.
:returns: returns a consistent format dict of sensor data grouped by
sensor type, which can be processed by Ceilometer.
"""
raise NotImplementedError()

View File

@ -39,15 +39,11 @@ from ironic.drivers.modules.irmc import inspect as irmc_inspect
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers.modules.msftocs import management as msftocs_management
from ironic.drivers.modules.msftocs import power as msftocs_power
from ironic.drivers.modules import pxe
from ironic.drivers.modules import seamicro
from ironic.drivers.modules import snmp
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
# For backward compatibility
@ -110,32 +106,6 @@ class PXEAndIPMINativeDriver(base.BaseDriver):
self.raid = agent.AgentRAID()
class PXEAndSeaMicroDriver(base.BaseDriver):
"""PXE + SeaMicro driver.
This driver implements the `core` functionality, combining
:class:`ironic.drivers.modules.seamicro.Power` 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('seamicroclient'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
reason=_("Unable to import seamicroclient library"))
self.power = seamicro.Power()
self.boot = pxe.PXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy()
self.management = seamicro.Management()
self.vendor = seamicro.VendorPassthru()
self.console = seamicro.ShellinaboxConsole()
class PXEAndIloDriver(base.BaseDriver):
"""PXE + Ilo Driver using IloClient interface.
@ -206,52 +176,6 @@ class PXEAndIRMCDriver(base.BaseDriver):
self.inspect = irmc_inspect.IRMCInspect()
class PXEAndVirtualBoxDriver(base.BaseDriver):
"""PXE + VirtualBox driver.
NOTE: This driver is meant only for testing environments.
This driver implements the `core` functionality, combining
:class:`ironic.drivers.virtualbox.VirtualBoxPower` for power on/off and
reboot of VirtualBox virtual machines, 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('pyremotevbox'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
reason=_("Unable to import pyremotevbox library"))
self.power = virtualbox.VirtualBoxPower()
self.boot = pxe.PXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy()
self.management = virtualbox.VirtualBoxManagement()
self.raid = agent.AgentRAID()
class PXEAndMSFTOCSDriver(base.BaseDriver):
"""PXE + MSFT OCS driver.
This driver implements the `core` functionality, combining
:class:`ironic.drivers.modules.msftocs.power.MSFTOCSPower` 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):
self.power = msftocs_power.MSFTOCSPower()
self.boot = pxe.PXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy()
self.management = msftocs_management.MSFTOCSManagement()
class PXEAndUcsDriver(base.BaseDriver):
"""PXE + Cisco UCSM driver.

View File

@ -4776,12 +4776,6 @@ class ManagerTestProperties(mgr_utils.ServiceSetUpMixin,
'deploy_forces_oob_reboot']
self._check_driver_properties("fake_pxe", expected)
def test_driver_properties_fake_seamicro(self):
expected = ['seamicro_api_endpoint', 'seamicro_password',
'seamicro_server_id', 'seamicro_username',
'seamicro_api_version', 'seamicro_terminal_port']
self._check_driver_properties("fake_seamicro", expected)
def test_driver_properties_fake_snmp(self):
expected = ['snmp_driver', 'snmp_address', 'snmp_port', 'snmp_version',
'snmp_community', 'snmp_security', 'snmp_outlet']
@ -4813,14 +4807,6 @@ class ManagerTestProperties(mgr_utils.ServiceSetUpMixin,
'deploy_forces_oob_reboot']
self._check_driver_properties("pxe_ssh", expected)
def test_driver_properties_pxe_seamicro(self):
expected = ['deploy_kernel', 'deploy_ramdisk',
'seamicro_api_endpoint', 'seamicro_password',
'seamicro_server_id', 'seamicro_username',
'seamicro_api_version', 'seamicro_terminal_port',
'deploy_forces_oob_reboot']
self._check_driver_properties("pxe_seamicro", expected)
def test_driver_properties_pxe_snmp(self):
expected = ['deploy_kernel', 'deploy_ramdisk',
'snmp_driver', 'snmp_address', 'snmp_port', 'snmp_version',

View File

@ -89,15 +89,6 @@ def get_test_pxe_instance_info():
}
def get_test_seamicro_info():
return {
"seamicro_api_endpoint": "http://1.2.3.4",
"seamicro_username": "admin",
"seamicro_password": "fake",
"seamicro_server_id": "0/0",
}
def get_test_ilo_info():
return {
"ilo_address": "1.2.3.4",
@ -127,15 +118,6 @@ def get_test_irmc_info():
}
def get_test_msftocs_info():
return {
"msftocs_base_url": "http://fakehost:8000",
"msftocs_username": "admin",
"msftocs_password": "fake",
"msftocs_blade_id": 1,
}
def get_test_agent_instance_info():
return {
'image_source': 'fake-image',

View File

@ -1,110 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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 MSFT OCS common functions
"""
import mock
from ironic.common import exception
from ironic.conductor import task_manager
from ironic.drivers.modules.msftocs import common as msftocs_common
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_msftocs_info()
class MSFTOCSCommonTestCase(db_base.DbTestCase):
def setUp(self):
super(MSFTOCSCommonTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_msftocs')
self.info = INFO_DICT
self.node = obj_utils.create_test_node(self.context,
driver='fake_msftocs',
driver_info=self.info)
def test_get_client_info(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_info
(client, blade_id) = msftocs_common.get_client_info(driver_info)
self.assertEqual(driver_info['msftocs_base_url'], client._base_url)
self.assertEqual(driver_info['msftocs_username'], client._username)
self.assertEqual(driver_info['msftocs_password'], client._password)
self.assertEqual(driver_info['msftocs_blade_id'], blade_id)
@mock.patch.object(msftocs_common, '_is_valid_url', autospec=True)
def test_parse_driver_info(self, mock_is_valid_url):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
msftocs_common.parse_driver_info(task.node)
mock_is_valid_url.assert_called_once_with(
task.node.driver_info['msftocs_base_url'])
def test_parse_driver_info_fail_missing_param(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
del task.node.driver_info['msftocs_base_url']
self.assertRaises(exception.MissingParameterValue,
msftocs_common.parse_driver_info,
task.node)
def test_parse_driver_info_fail_bad_url(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info['msftocs_base_url'] = "bad-url"
self.assertRaises(exception.InvalidParameterValue,
msftocs_common.parse_driver_info,
task.node)
def test_parse_driver_info_fail_bad_blade_id_type(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info['msftocs_blade_id'] = "bad-blade-id"
self.assertRaises(exception.InvalidParameterValue,
msftocs_common.parse_driver_info,
task.node)
def test_parse_driver_info_fail_bad_blade_id_value(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info['msftocs_blade_id'] = 0
self.assertRaises(exception.InvalidParameterValue,
msftocs_common.parse_driver_info,
task.node)
def test__is_valid_url(self):
self.assertIs(True, msftocs_common._is_valid_url("http://fake.com"))
self.assertIs(
True, msftocs_common._is_valid_url("http://www.fake.com"))
self.assertIs(True, msftocs_common._is_valid_url("http://FAKE.com"))
self.assertIs(True, msftocs_common._is_valid_url("http://fake"))
self.assertIs(
True, msftocs_common._is_valid_url("http://fake.com/blah"))
self.assertIs(True, msftocs_common._is_valid_url("http://localhost"))
self.assertIs(True, msftocs_common._is_valid_url("https://fake.com"))
self.assertIs(True, msftocs_common._is_valid_url("http://10.0.0.1"))
self.assertIs(False, msftocs_common._is_valid_url("bad-url"))
self.assertIs(False, msftocs_common._is_valid_url("http://.bad-url"))
self.assertIs(False, msftocs_common._is_valid_url("http://bad-url$"))
self.assertIs(False, msftocs_common._is_valid_url("http://$bad-url"))
self.assertIs(False, msftocs_common._is_valid_url("http://bad$url"))
self.assertIs(False, msftocs_common._is_valid_url(None))
self.assertIs(False, msftocs_common._is_valid_url(0))

View File

@ -1,132 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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 MSFT OCS ManagementInterface
"""
import mock
from ironic.common import boot_devices
from ironic.common import exception
from ironic.conductor import task_manager
from ironic.drivers.modules.msftocs import common as msftocs_common
from ironic.drivers.modules.msftocs import msftocsclient
from ironic.drivers import utils as drivers_utils
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_msftocs_info()
class MSFTOCSManagementTestCase(db_base.DbTestCase):
def setUp(self):
super(MSFTOCSManagementTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_msftocs')
self.info = INFO_DICT
self.node = obj_utils.create_test_node(self.context,
driver='fake_msftocs',
driver_info=self.info)
def test_get_properties(self):
expected = msftocs_common.REQUIRED_PROPERTIES
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(expected, task.driver.get_properties())
@mock.patch.object(msftocs_common, 'parse_driver_info', 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(msftocs_common, 'parse_driver_info', 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)
def test_get_supported_boot_devices(self):
expected = [boot_devices.PXE, boot_devices.DISK, boot_devices.BIOS]
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)))
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def _test_set_boot_device_one_time(self, persistent, uefi,
mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
if uefi:
drivers_utils.add_node_capability(task, 'boot_mode', 'uefi')
task.driver.management.set_boot_device(
task, boot_devices.PXE, persistent)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_next_boot.assert_called_once_with(
blade_id, msftocsclient.BOOT_TYPE_FORCE_PXE, persistent, uefi)
def test_set_boot_device_one_time(self):
self._test_set_boot_device_one_time(False, False)
def test_set_boot_device_persistent(self):
self._test_set_boot_device_one_time(True, False)
def test_set_boot_device_uefi(self):
self._test_set_boot_device_one_time(True, True)
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(msftocs_common, 'get_client_info', autospec=True)
def test_get_boot_device(self, mock_gci):
expected = {'boot_device': boot_devices.DISK, 'persistent': None}
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
force_hdd = msftocsclient.BOOT_TYPE_FORCE_DEFAULT_HDD
mock_c.get_next_boot.return_value = force_hdd
self.assertEqual(expected,
task.driver.management.get_boot_device(task))
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.get_next_boot.assert_called_once_with(blade_id)
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,182 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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 MSFT OCS REST API client
"""
import mock
import requests
from requests import exceptions as requests_exceptions
from ironic.common import exception
from ironic.drivers.modules.msftocs import msftocsclient
from ironic.tests import base
FAKE_BOOT_RESPONSE = (
'<BootResponse xmlns="%s" '
'xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
'<completionCode>Success</completionCode>'
'<apiVersion>1</apiVersion>'
'<statusDescription>Success</statusDescription>'
'<bladeNumber>1</bladeNumber>'
'<nextBoot>ForcePxe</nextBoot>'
'</BootResponse>') % msftocsclient.WCSNS
FAKE_BLADE_RESPONSE = (
'<BladeResponse xmlns="%s" '
'xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
'<completionCode>Success</completionCode>'
'<apiVersion>1</apiVersion>'
'<statusDescription/>'
'<bladeNumber>1</bladeNumber>'
'</BladeResponse>') % msftocsclient.WCSNS
FAKE_POWER_STATE_RESPONSE = (
'<PowerStateResponse xmlns="%s" '
'xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
'<completionCode>Success</completionCode>'
'<apiVersion>1</apiVersion>'
'<statusDescription>Blade Power is On, firmware decompressed'
'</statusDescription>'
'<bladeNumber>1</bladeNumber>'
'<Decompression>0</Decompression>'
'<powerState>ON</powerState>'
'</PowerStateResponse>') % msftocsclient.WCSNS
FAKE_BLADE_STATE_RESPONSE = (
'<BladeStateResponse xmlns="%s" '
'xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
'<completionCode>Success</completionCode>'
'<apiVersion>1</apiVersion>'
'<statusDescription/>'
'<bladeNumber>1</bladeNumber>'
'<bladeState>ON</bladeState>'
'</BladeStateResponse>') % msftocsclient.WCSNS
class MSFTOCSClientApiTestCase(base.TestCase):
def setUp(self):
super(MSFTOCSClientApiTestCase, self).setUp()
self._fake_base_url = "http://fakehost:8000"
self._fake_username = "admin"
self._fake_password = 'fake'
self._fake_blade_id = 1
self._client = msftocsclient.MSFTOCSClientApi(
self._fake_base_url, self._fake_username, self._fake_password)
@mock.patch.object(requests, 'get', autospec=True)
def test__exec_cmd(self, mock_get):
fake_response_text = 'fake_response_text'
fake_rel_url = 'fake_rel_url'
mock_get.return_value.text = 'fake_response_text'
self.assertEqual(fake_response_text,
self._client._exec_cmd(fake_rel_url))
mock_get.assert_called_once_with(
self._fake_base_url + "/" + fake_rel_url, auth=mock.ANY)
@mock.patch.object(requests, 'get', autospec=True)
def test__exec_cmd_http_get_fail(self, mock_get):
fake_rel_url = 'fake_rel_url'
mock_get.side_effect = requests_exceptions.ConnectionError('x')
self.assertRaises(exception.MSFTOCSClientApiException,
self._client._exec_cmd,
fake_rel_url)
mock_get.assert_called_once_with(
self._fake_base_url + "/" + fake_rel_url, auth=mock.ANY)
def test__check_completion_code(self):
et = self._client._check_completion_code(FAKE_BOOT_RESPONSE)
self.assertEqual('{%s}BootResponse' % msftocsclient.WCSNS, et.tag)
def test__check_completion_code_fail(self):
self.assertRaises(exception.MSFTOCSClientApiException,
self._client._check_completion_code,
'<fake xmlns="%s"></fake>' % msftocsclient.WCSNS)
def test__check_completion_with_bad_completion_code_fail(self):
self.assertRaises(exception.MSFTOCSClientApiException,
self._client._check_completion_code,
'<fake xmlns="%s">'
'<completionCode>Fail</completionCode>'
'</fake>' % msftocsclient.WCSNS)
def test__check_completion_code_xml_parsing_fail(self):
self.assertRaises(exception.MSFTOCSClientApiException,
self._client._check_completion_code,
'bad_xml')
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_get_blade_state(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BLADE_STATE_RESPONSE
self.assertEqual(
msftocsclient.POWER_STATUS_ON,
self._client.get_blade_state(self._fake_blade_id))
mock_exec_cmd.assert_called_once_with(
self._client, "GetBladeState?bladeId=%d" % self._fake_blade_id)
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_set_blade_on(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BLADE_RESPONSE
self._client.set_blade_on(self._fake_blade_id)
mock_exec_cmd.assert_called_once_with(
self._client, "SetBladeOn?bladeId=%d" % self._fake_blade_id)
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_set_blade_off(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BLADE_RESPONSE
self._client.set_blade_off(self._fake_blade_id)
mock_exec_cmd.assert_called_once_with(
self._client, "SetBladeOff?bladeId=%d" % self._fake_blade_id)
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_set_blade_power_cycle(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BLADE_RESPONSE
self._client.set_blade_power_cycle(self._fake_blade_id)
mock_exec_cmd.assert_called_once_with(
self._client,
"SetBladeActivePowerCycle?bladeId=%d&offTime=0" %
self._fake_blade_id)
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_get_next_boot(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BOOT_RESPONSE
self.assertEqual(
msftocsclient.BOOT_TYPE_FORCE_PXE,
self._client.get_next_boot(self._fake_blade_id))
mock_exec_cmd.assert_called_once_with(
self._client, "GetNextBoot?bladeId=%d" % self._fake_blade_id)
@mock.patch.object(
msftocsclient.MSFTOCSClientApi, '_exec_cmd', autospec=True)
def test_set_next_boot(self, mock_exec_cmd):
mock_exec_cmd.return_value = FAKE_BOOT_RESPONSE
self._client.set_next_boot(self._fake_blade_id,
msftocsclient.BOOT_TYPE_FORCE_PXE)
mock_exec_cmd.assert_called_once_with(
self._client,
"SetNextBoot?bladeId=%(blade_id)d&bootType=%(boot_type)d&"
"uefi=%(uefi)s&persistent=%(persistent)s" %
{"blade_id": self._fake_blade_id,
"boot_type": msftocsclient.BOOT_TYPE_FORCE_PXE,
"uefi": "true", "persistent": "true"})

View File

@ -1,163 +0,0 @@
# Copyright 2015 Cloudbase Solutions Srl
# 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 MSFT OCS PowerInterface
"""
import mock
from ironic.common import exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers.modules.msftocs import common as msftocs_common
from ironic.drivers.modules.msftocs import msftocsclient
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_msftocs_info()
class MSFTOCSPowerTestCase(db_base.DbTestCase):
def setUp(self):
super(MSFTOCSPowerTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_msftocs')
self.info = INFO_DICT
self.node = obj_utils.create_test_node(self.context,
driver='fake_msftocs',
driver_info=self.info)
def test_get_properties(self):
expected = msftocs_common.REQUIRED_PROPERTIES
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(expected, task.driver.get_properties())
@mock.patch.object(msftocs_common, 'parse_driver_info', 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(msftocs_common, 'parse_driver_info', 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(msftocs_common, 'get_client_info', autospec=True)
def test_get_power_state(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
mock_c.get_blade_state.return_value = msftocsclient.POWER_STATUS_ON
self.assertEqual(states.POWER_ON,
task.driver.power.get_power_state(task))
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.get_blade_state.assert_called_once_with(blade_id)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_set_power_state_on(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
task.driver.power.set_power_state(task, states.POWER_ON)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_blade_on.assert_called_once_with(blade_id)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_set_power_state_off(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
task.driver.power.set_power_state(task, states.POWER_OFF)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_blade_off.assert_called_once_with(blade_id)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_set_power_state_blade_on_fail(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
ex = exception.MSFTOCSClientApiException('x')
mock_c.set_blade_on.side_effect = ex
pstate = states.POWER_ON
self.assertRaises(exception.PowerStateFailure,
task.driver.power.set_power_state,
task, pstate)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_blade_on.assert_called_once_with(blade_id)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_set_power_state_invalid_parameter_fail(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
pstate = states.ERROR
self.assertRaises(exception.InvalidParameterValue,
task.driver.power.set_power_state,
task, pstate)
mock_gci.assert_called_once_with(task.node.driver_info)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_reboot(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
task.driver.power.reboot(task)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_blade_power_cycle.assert_called_once_with(blade_id)
@mock.patch.object(msftocs_common, 'get_client_info', autospec=True)
def test_reboot_fail(self, mock_gci):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
mock_c = mock.MagicMock(spec=msftocsclient.MSFTOCSClientApi)
blade_id = task.node.driver_info['msftocs_blade_id']
mock_gci.return_value = (mock_c, blade_id)
ex = exception.MSFTOCSClientApiException('x')
mock_c.set_blade_power_cycle.side_effect = ex
self.assertRaises(exception.PowerStateFailure,
task.driver.power.reboot,
task)
mock_gci.assert_called_once_with(task.node.driver_info)
mock_c.set_blade_power_cycle.assert_called_once_with(blade_id)

View File

@ -1,689 +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 Ironic SeaMicro driver."""
import mock
from oslo_utils import uuidutils
from seamicroclient import client as seamicro_client
from seamicroclient import exceptions as seamicro_client_exception
from six.moves import http_client
from ironic.common import boot_devices
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 console_utils
from ironic.drivers.modules import seamicro
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_seamicro_info()
class Fake_Server(object):
def __init__(self, active=False, *args, **kwargs):
self.active = active
self.nic = {'0': {'untaggedVlan': ''}}
def power_on(self):
self.active = True
def power_off(self, force=False):
self.active = False
def reset(self):
self.active = True
def set_untagged_vlan(self, vlan_id):
return
def attach_volume(self, volume_id):
return
def detach_volume(self):
return
def set_boot_order(self, boot_order):
return
def refresh(self, wait=0):
return self
class Fake_Volume(object):
def __init__(self, id=None, *args, **kwargs):
if id is None:
self.id = "%s/%s/%s" % ("0", "ironic-p6-6",
uuidutils.generate_uuid())
else:
self.id = id
class Fake_Pool(object):
def __init__(self, freeSize=None, *args, **kwargs):
self.freeSize = freeSize
class SeaMicroValidateParametersTestCase(db_base.DbTestCase):
def test__parse_driver_info_good(self):
# make sure we get back the expected things
node = obj_utils.get_test_node(
self.context,
driver='fake_seamicro',
driver_info=INFO_DICT)
info = seamicro._parse_driver_info(node)
self.assertEqual('http://1.2.3.4', info['api_endpoint'])
self.assertEqual('admin', info['username'])
self.assertEqual('fake', info['password'])
self.assertEqual('0/0', info['server_id'])
self.assertEqual('1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
info['uuid'])
def test__parse_driver_info_missing_api_endpoint(self):
# make sure error is raised when info is missing
info = dict(INFO_DICT)
del info['seamicro_api_endpoint']
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
seamicro._parse_driver_info,
node)
def test__parse_driver_info_missing_username(self):
# make sure error is raised when info is missing
info = dict(INFO_DICT)
del info['seamicro_username']
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
seamicro._parse_driver_info,
node)
def test__parse_driver_info_missing_password(self):
# make sure error is raised when info is missing
info = dict(INFO_DICT)
del info['seamicro_password']
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
seamicro._parse_driver_info,
node)
def test__parse_driver_info_missing_server_id(self):
# make sure error is raised when info is missing
info = dict(INFO_DICT)
del info['seamicro_server_id']
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.MissingParameterValue,
seamicro._parse_driver_info,
node)
def test__parse_driver_info_empty_terminal_port(self):
info = dict(INFO_DICT)
info['seamicro_terminal_port'] = ''
node = obj_utils.get_test_node(self.context, driver_info=info)
self.assertRaises(exception.InvalidParameterValue,
seamicro._parse_driver_info,
node)
@mock.patch('eventlet.greenthread.sleep', lambda n: None)
class SeaMicroPrivateMethodsTestCase(db_base.DbTestCase):
def setUp(self):
super(SeaMicroPrivateMethodsTestCase, self).setUp()
n = {
'driver': 'fake_seamicro',
'driver_info': INFO_DICT
}
self.node = obj_utils.create_test_node(self.context, **n)
self.Server = Fake_Server
self.Volume = Fake_Volume
self.Pool = Fake_Pool
self.config(action_timeout=0, group='seamicro')
self.config(max_retry=2, group='seamicro')
self.info = seamicro._parse_driver_info(self.node)
@mock.patch.object(seamicro_client, "Client", autospec=True)
def test__get_client(self, mock_client):
args = {'username': self.info['username'],
'password': self.info['password'],
'auth_url': self.info['api_endpoint']}
seamicro._get_client(**self.info)
mock_client.assert_called_once_with(self.info['api_version'], **args)
@mock.patch.object(seamicro_client, "Client", autospec=True)
def test__get_client_fail(self, mock_client):
args = {'username': self.info['username'],
'password': self.info['password'],
'auth_url': self.info['api_endpoint']}
mock_client.side_effect = seamicro_client_exception.UnsupportedVersion
self.assertRaises(exception.InvalidParameterValue,
seamicro._get_client,
**self.info)
mock_client.assert_called_once_with(self.info['api_version'], **args)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__get_power_status_on(self, mock_get_server):
mock_get_server.return_value = self.Server(active=True)
pstate = seamicro._get_power_status(self.node)
self.assertEqual(states.POWER_ON, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__get_power_status_off(self, mock_get_server):
mock_get_server.return_value = self.Server(active=False)
pstate = seamicro._get_power_status(self.node)
self.assertEqual(states.POWER_OFF, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__get_power_status_error(self, mock_get_server):
mock_get_server.return_value = self.Server(active=None)
pstate = seamicro._get_power_status(self.node)
self.assertEqual(states.ERROR, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__power_on_good(self, mock_get_server):
mock_get_server.return_value = self.Server(active=False)
pstate = seamicro._power_on(self.node)
self.assertEqual(states.POWER_ON, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__power_on_fail(self, mock_get_server):
def fake_power_on():
return
server = self.Server(active=False)
server.power_on = fake_power_on
mock_get_server.return_value = server
pstate = seamicro._power_on(self.node)
self.assertEqual(states.ERROR, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__power_off_good(self, mock_get_server):
mock_get_server.return_value = self.Server(active=True)
pstate = seamicro._power_off(self.node)
self.assertEqual(states.POWER_OFF, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__power_off_fail(self, mock_get_server):
def fake_power_off():
return
server = self.Server(active=True)
server.power_off = fake_power_off
mock_get_server.return_value = server
pstate = seamicro._power_off(self.node)
self.assertEqual(states.ERROR, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__reboot_good(self, mock_get_server):
mock_get_server.return_value = self.Server(active=True)
pstate = seamicro._reboot(self.node)
self.assertEqual(states.POWER_ON, pstate)
@mock.patch.object(seamicro, "_get_server", autospec=True)
def test__reboot_fail(self, mock_get_server):
def fake_reboot():
return
server = self.Server(active=False)
server.reset = fake_reboot
mock_get_server.return_value = server
pstate = seamicro._reboot(self.node)
self.assertEqual(states.ERROR, pstate)
@mock.patch.object(seamicro, "_get_volume", autospec=True)
def test__validate_fail(self, mock_get_volume):
volume_id = "0/p6-6/vol1"
volume = self.Volume()
volume.id = volume_id
mock_get_volume.return_value = volume
self.assertRaises(exception.InvalidParameterValue,
seamicro._validate_volume, self.info, volume_id)
@mock.patch.object(seamicro, "_get_volume", autospec=True)
def test__validate_good(self, mock_get_volume):
volume = self.Volume()
mock_get_volume.return_value = volume
valid = seamicro._validate_volume(self.info, volume.id)
self.assertTrue(valid)
@mock.patch.object(seamicro, "_get_pools", autospec=True)
def test__create_volume_fail(self, mock_get_pools):
mock_get_pools.return_value = None
self.assertRaises(exception.IronicException,
seamicro._create_volume,
self.info, 2)
@mock.patch.object(seamicro, "_get_pools", autospec=True)
@mock.patch.object(seamicro, "_get_client", autospec=True)
def test__create_volume_good(self, mock_get_client, mock_get_pools):
pools = [self.Pool(1), self.Pool(6), self.Pool(5)]
mock_seamicro_volumes = mock.MagicMock(spec_set=['create'])
mock_get_client.return_value = mock.MagicMock(
volumes=mock_seamicro_volumes, spec_set=['volumes'])
mock_get_pools.return_value = pools
seamicro._create_volume(self.info, 2)
class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
def setUp(self):
super(SeaMicroPowerDriverTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_seamicro')
self.driver = driver_factory.get_driver('fake_seamicro')
self.node = obj_utils.create_test_node(self.context,
driver='fake_seamicro',
driver_info=INFO_DICT)
self.get_server_patcher = mock.patch.object(seamicro, '_get_server',
autospec=True)
self.get_server_mock = None
self.Server = Fake_Server
self.Volume = Fake_Volume
self.info = seamicro._parse_driver_info(self.node)
def test_get_properties(self):
expected = seamicro.COMMON_PROPERTIES
with task_manager.acquire(self.context, self.node['uuid'],
shared=True) as task:
self.assertEqual(expected, task.driver.power.get_properties())
expected = (list(seamicro.COMMON_PROPERTIES) +
list(seamicro.CONSOLE_PROPERTIES))
console_properties = task.driver.console.get_properties().keys()
self.assertEqual(sorted(expected), sorted(console_properties))
self.assertEqual(sorted(expected),
sorted(task.driver.get_properties().keys()))
def test_vendor_routes(self):
expected = ['set_node_vlan_id', 'attach_volume']
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(vendor_routes))
def test_driver_routes(self):
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({}, driver_routes)
@mock.patch.object(seamicro, '_parse_driver_info', autospec=True)
def test_power_interface_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(seamicro, '_parse_driver_info', autospec=True)
def test_power_interface_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)
@mock.patch.object(seamicro, '_reboot', autospec=True)
def test_reboot(self, mock_reboot):
mock_reboot.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
task.driver.power.reboot(task)
mock_reboot.assert_called_once_with(task.node)
def test_set_power_state_bad_state(self):
self.get_server_mock = self.get_server_patcher.start()
self.get_server_mock.return_value = self.Server()
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.IronicException,
task.driver.power.set_power_state,
task, "BAD_PSTATE")
self.get_server_patcher.stop()
@mock.patch.object(seamicro, '_power_on', autospec=True)
def test_set_power_state_on_good(self, mock_power_on):
mock_power_on.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
task.driver.power.set_power_state(task, states.POWER_ON)
mock_power_on.assert_called_once_with(task.node)
@mock.patch.object(seamicro, '_power_on', autospec=True)
def test_set_power_state_on_fail(self, mock_power_on):
mock_power_on.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.PowerStateFailure,
task.driver.power.set_power_state,
task, states.POWER_ON)
mock_power_on.assert_called_once_with(task.node)
@mock.patch.object(seamicro, '_power_off', autospec=True)
def test_set_power_state_off_good(self, mock_power_off):
mock_power_off.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
task.driver.power.set_power_state(task, states.POWER_OFF)
mock_power_off.assert_called_once_with(task.node)
@mock.patch.object(seamicro, '_power_off', autospec=True)
def test_set_power_state_off_fail(self, mock_power_off):
mock_power_off.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.PowerStateFailure,
task.driver.power.set_power_state,
task, states.POWER_OFF)
mock_power_off.assert_called_once_with(task.node)
@mock.patch.object(seamicro, '_parse_driver_info', autospec=True)
def test_vendor_passthru_validate_good(self, mock_info):
with task_manager.acquire(self.context, self.node['uuid'],
shared=True) as task:
for method in task.driver.vendor.vendor_routes:
task.driver.vendor.validate(task, **{'method': method})
self.assertEqual(len(task.driver.vendor.vendor_routes),
mock_info.call_count)
@mock.patch.object(seamicro, '_parse_driver_info', autospec=True)
def test_vendor_passthru_validate_parse_driver_info_fail(self, mock_info):
mock_info.side_effect = exception.InvalidParameterValue("bad")
with task_manager.acquire(self.context, self.node['uuid'],
shared=True) as task:
method = list(task.driver.vendor.vendor_routes)[0]
self.assertRaises(exception.InvalidParameterValue,
task.driver.vendor.validate,
task, **{'method': method})
mock_info.assert_called_once_with(task.node)
@mock.patch.object(seamicro, '_get_server', autospec=True)
def test_set_node_vlan_id_good(self, mock_get_server):
vlan_id = "12"
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'vlan_id': vlan_id}
task.driver.vendor.set_node_vlan_id(task, **kwargs)
mock_get_server.assert_called_once_with(self.info)
def test_set_node_vlan_id_no_input(self):
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.vendor.set_node_vlan_id,
task, **{})
@mock.patch.object(seamicro, '_get_server', autospec=True)
def test_set_node_vlan_id_fail(self, mock_get_server):
def fake_set_untagged_vlan(self, **kwargs):
raise seamicro_client_exception.ClientException(
http_client.INTERNAL_SERVER_ERROR)
vlan_id = "12"
server = self.Server(active="true")
server.set_untagged_vlan = fake_set_untagged_vlan
mock_get_server.return_value = server
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'vlan_id': vlan_id}
self.assertRaises(exception.IronicException,
task.driver.vendor.set_node_vlan_id,
task, **kwargs)
mock_get_server.assert_called_once_with(self.info)
@mock.patch.object(seamicro, '_get_server', autospec=True)
@mock.patch.object(seamicro, '_validate_volume', autospec=True)
def test_attach_volume_with_volume_id_good(self, mock_validate_volume,
mock_get_server):
volume_id = '0/ironic-p6-1/vol1'
mock_validate_volume.return_value = True
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'volume_id': volume_id}
task.driver.vendor.attach_volume(task, **kwargs)
mock_get_server.assert_called_once_with(self.info)
@mock.patch.object(seamicro, '_get_server', autospec=True)
@mock.patch.object(seamicro, '_get_volume', autospec=True)
def test_attach_volume_with_invalid_volume_id_fail(self,
mock_get_volume,
mock_get_server):
volume_id = '0/p6-1/vol1'
mock_get_volume.return_value = self.Volume(volume_id)
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'volume_id': volume_id}
self.assertRaises(exception.InvalidParameterValue,
task.driver.vendor.attach_volume,
task, **kwargs)
@mock.patch.object(seamicro, '_get_server', autospec=True)
@mock.patch.object(seamicro, '_validate_volume', autospec=True)
def test_attach_volume_fail(self, mock_validate_volume,
mock_get_server):
def fake_attach_volume(self, **kwargs):
raise seamicro_client_exception.ClientException(
http_client.INTERNAL_SERVER_ERROR)
volume_id = '0/p6-1/vol1'
mock_validate_volume.return_value = True
server = self.Server(active="true")
server.attach_volume = fake_attach_volume
mock_get_server.return_value = server
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'volume_id': volume_id}
self.assertRaises(exception.IronicException,
task.driver.vendor.attach_volume,
task, **kwargs)
mock_get_server.assert_called_once_with(self.info)
@mock.patch.object(seamicro, '_get_server', autospec=True)
@mock.patch.object(seamicro, '_validate_volume', autospec=True)
@mock.patch.object(seamicro, '_create_volume', autospec=True)
def test_attach_volume_with_volume_size_good(self, mock_create_volume,
mock_validate_volume,
mock_get_server):
volume_id = '0/ironic-p6-1/vol1'
volume_size = 2
mock_create_volume.return_value = volume_id
mock_validate_volume.return_value = True
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
kwargs = {'volume_size': volume_size}
task.driver.vendor.attach_volume(task, **kwargs)
mock_get_server.assert_called_once_with(self.info)
mock_create_volume.assert_called_once_with(self.info, volume_size)
def test_attach_volume_with_no_input_fail(self):
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.vendor.attach_volume, task,
**{})
@mock.patch.object(seamicro, '_get_server', autospec=True)
def test_set_boot_device_good(self, mock_get_server):
boot_device = "disk"
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
task.driver.management.set_boot_device(task, boot_device)
mock_get_server.assert_called_once_with(self.info)
@mock.patch.object(seamicro, '_get_server', autospec=True)
def test_set_boot_device_invalid_device_fail(self, mock_get_server):
boot_device = "invalid_device"
mock_get_server.return_value = self.Server(active="true")
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.set_boot_device,
task, boot_device)
@mock.patch.object(seamicro, '_get_server', autospec=True)
def test_set_boot_device_fail(self, mock_get_server):
def fake_set_boot_order(self, **kwargs):
raise seamicro_client_exception.ClientException(
http_client.INTERNAL_SERVER_ERROR)
boot_device = "pxe"
server = self.Server(active="true")
server.set_boot_order = fake_set_boot_order
mock_get_server.return_value = server
with task_manager.acquire(self.context, self.info['uuid'],
shared=False) as task:
self.assertRaises(exception.IronicException,
task.driver.management.set_boot_device,
task, boot_device)
mock_get_server.assert_called_once_with(self.info)
def test_management_interface_get_supported_boot_devices(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
expected = [boot_devices.PXE, boot_devices.DISK]
self.assertEqual(sorted(expected), sorted(task.driver.management.
get_supported_boot_devices(task)))
def test_management_interface_get_boot_device(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
expected = {'boot_device': None, 'persistent': None}
self.assertEqual(expected,
task.driver.management.get_boot_device(task))
def test_management_interface_validate_good(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
task.driver.management.validate(task)
def test_management_interface_validate_fail(self):
# Missing SEAMICRO driver_info information
node = obj_utils.create_test_node(self.context,
uuid=uuidutils.generate_uuid(),
driver='fake_seamicro')
with task_manager.acquire(self.context, node.uuid) as task:
self.assertRaises(exception.MissingParameterValue,
task.driver.management.validate, task)
class SeaMicroDriverTestCase(db_base.DbTestCase):
def setUp(self):
super(SeaMicroDriverTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_seamicro')
self.driver = driver_factory.get_driver('fake_seamicro')
self.node = obj_utils.create_test_node(self.context,
driver='fake_seamicro',
driver_info=INFO_DICT)
self.get_server_patcher = mock.patch.object(seamicro, '_get_server',
autospec=True)
self.get_server_mock = None
self.Server = Fake_Server
self.Volume = Fake_Volume
self.info = seamicro._parse_driver_info(self.node)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test_start_console(self, mock_exec):
mock_exec.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.driver.console.start_console(task)
mock_exec.assert_called_once_with(self.info['uuid'],
self.info['port'],
mock.ANY)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test_start_console_fail(self, mock_exec):
mock_exec.side_effect = exception.ConsoleSubprocessFailed(
error='error')
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.ConsoleSubprocessFailed,
self.driver.console.start_console,
task)
@mock.patch.object(console_utils, 'stop_shellinabox_console',
autospec=True)
def test_stop_console(self, mock_exec):
mock_exec.return_value = None
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.driver.console.stop_console(task)
mock_exec.assert_called_once_with(self.info['uuid'])
@mock.patch.object(console_utils, 'stop_shellinabox_console',
autospec=True)
def test_stop_console_fail(self, mock_stop):
mock_stop.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.ConsoleError,
self.driver.console.stop_console,
task)
mock_stop.assert_called_once_with(self.node.uuid)
@mock.patch.object(console_utils, 'start_shellinabox_console',
autospec=True)
def test_start_console_fail_nodir(self, mock_exec):
mock_exec.side_effect = exception.ConsoleError()
with task_manager.acquire(self.context,
self.node.uuid) as task:
self.assertRaises(exception.ConsoleError,
self.driver.console.start_console,
task)
mock_exec.assert_called_once_with(self.node.uuid, mock.ANY, mock.ANY)
@mock.patch.object(console_utils, 'get_shellinabox_console_url',
autospec=True)
def test_get_console(self, mock_exec):
url = 'http://localhost:4201'
mock_exec.return_value = url
expected = {'type': 'shellinabox', 'url': url}
with task_manager.acquire(self.context,
self.node.uuid) as task:
console_info = self.driver.console.get_console(task)
self.assertEqual(expected, console_info)
mock_exec.assert_called_once_with(self.info['port'])

View File

@ -1,420 +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 VirtualBox Driver Modules."""
import mock
from pyremotevbox import exception as pyremotevbox_exc
from pyremotevbox import vbox as pyremotevbox_vbox
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 import virtualbox
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
INFO_DICT = {
'virtualbox_vmname': 'baremetal1',
'virtualbox_host': '10.0.2.2',
'virtualbox_username': 'username',
'virtualbox_password': 'password',
'virtualbox_port': 12345,
}
class VirtualBoxMethodsTestCase(db_base.DbTestCase):
def setUp(self):
super(VirtualBoxMethodsTestCase, self).setUp()
driver_info = INFO_DICT.copy()
mgr_utils.mock_the_extension_manager(driver="fake_vbox")
self.node = obj_utils.create_test_node(self.context,
driver='fake_vbox',
driver_info=driver_info)
def test__parse_driver_info(self):
info = virtualbox._parse_driver_info(self.node)
self.assertEqual('baremetal1', info['vmname'])
self.assertEqual('10.0.2.2', info['host'])
self.assertEqual('username', info['username'])
self.assertEqual('password', info['password'])
self.assertEqual(12345, info['port'])
def test__parse_driver_info_missing_vmname(self):
del self.node.driver_info['virtualbox_vmname']
self.assertRaises(exception.MissingParameterValue,
virtualbox._parse_driver_info, self.node)
def test__parse_driver_info_missing_host(self):
del self.node.driver_info['virtualbox_host']
self.assertRaises(exception.MissingParameterValue,
virtualbox._parse_driver_info, self.node)
def test__parse_driver_info_invalid_port(self):
self.node.driver_info['virtualbox_port'] = 'invalid-port'
self.assertRaises(exception.InvalidParameterValue,
virtualbox._parse_driver_info, self.node)
def test__parse_driver_info_missing_port(self):
del self.node.driver_info['virtualbox_port']
info = virtualbox._parse_driver_info(self.node)
self.assertEqual(18083, info['port'])
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method(self, host_mock):
host_object_mock = mock.MagicMock(spec_set=['find_vm'])
func_mock = mock.MagicMock(spec_set=[])
vm_object_mock = mock.MagicMock(spec_set=['foo'], foo=func_mock)
host_mock.return_value = host_object_mock
host_object_mock.find_vm.return_value = vm_object_mock
func_mock.return_value = 'return-value'
return_value = virtualbox._run_virtualbox_method(
self.node, 'some-ironic-method', 'foo', 'args', kwarg='kwarg')
host_mock.assert_called_once_with(vmname='baremetal1',
host='10.0.2.2',
username='username',
password='password',
port=12345)
host_object_mock.find_vm.assert_called_once_with('baremetal1')
func_mock.assert_called_once_with('args', kwarg='kwarg')
self.assertEqual('return-value', return_value)
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method_get_host_fails(self, host_mock):
host_mock.side_effect = pyremotevbox_exc.PyRemoteVBoxException
self.assertRaises(exception.VirtualBoxOperationFailed,
virtualbox._run_virtualbox_method,
self.node, 'some-ironic-method', 'foo',
'args', kwarg='kwarg')
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method_find_vm_fails(self, host_mock):
host_object_mock = mock.MagicMock(spec_set=['find_vm'])
host_mock.return_value = host_object_mock
exc = pyremotevbox_exc.PyRemoteVBoxException
host_object_mock.find_vm.side_effect = exc
self.assertRaises(exception.VirtualBoxOperationFailed,
virtualbox._run_virtualbox_method,
self.node, 'some-ironic-method', 'foo', 'args',
kwarg='kwarg')
host_mock.assert_called_once_with(vmname='baremetal1',
host='10.0.2.2',
username='username',
password='password',
port=12345)
host_object_mock.find_vm.assert_called_once_with('baremetal1')
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method_func_fails(self, host_mock):
host_object_mock = mock.MagicMock(spec_set=['find_vm'])
host_mock.return_value = host_object_mock
func_mock = mock.MagicMock()
vm_object_mock = mock.MagicMock(spec_set=['foo'], foo=func_mock)
host_object_mock.find_vm.return_value = vm_object_mock
func_mock.side_effect = pyremotevbox_exc.PyRemoteVBoxException
self.assertRaises(exception.VirtualBoxOperationFailed,
virtualbox._run_virtualbox_method,
self.node, 'some-ironic-method', 'foo',
'args', kwarg='kwarg')
host_mock.assert_called_once_with(vmname='baremetal1',
host='10.0.2.2',
username='username',
password='password',
port=12345)
host_object_mock.find_vm.assert_called_once_with('baremetal1')
func_mock.assert_called_once_with('args', kwarg='kwarg')
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method_invalid_method(self, host_mock):
host_object_mock = mock.MagicMock(spec_set=['find_vm'])
host_mock.return_value = host_object_mock
vm_object_mock = mock.MagicMock(spec_set=[])
host_object_mock.find_vm.return_value = vm_object_mock
del vm_object_mock.foo
self.assertRaises(exception.InvalidParameterValue,
virtualbox._run_virtualbox_method,
self.node, 'some-ironic-method', 'foo',
'args', kwarg='kwarg')
host_mock.assert_called_once_with(vmname='baremetal1',
host='10.0.2.2',
username='username',
password='password',
port=12345)
host_object_mock.find_vm.assert_called_once_with('baremetal1')
@mock.patch.object(pyremotevbox_vbox, 'VirtualBoxHost', autospec=True)
def test__run_virtualbox_method_vm_wrong_power_state(self, host_mock):
host_object_mock = mock.MagicMock(spec_set=['find_vm'])
host_mock.return_value = host_object_mock
func_mock = mock.MagicMock(spec_set=[])
vm_object_mock = mock.MagicMock(spec_set=['foo'], foo=func_mock)
host_object_mock.find_vm.return_value = vm_object_mock
func_mock.side_effect = pyremotevbox_exc.VmInWrongPowerState
# _run_virtualbox_method() doesn't catch VmInWrongPowerState and
# lets caller handle it.
self.assertRaises(pyremotevbox_exc.VmInWrongPowerState,
virtualbox._run_virtualbox_method,
self.node, 'some-ironic-method', 'foo',
'args', kwarg='kwarg')
host_mock.assert_called_once_with(vmname='baremetal1',
host='10.0.2.2',
username='username',
password='password',
port=12345)
host_object_mock.find_vm.assert_called_once_with('baremetal1')
func_mock.assert_called_once_with('args', kwarg='kwarg')
class VirtualBoxPowerTestCase(db_base.DbTestCase):
def setUp(self):
super(VirtualBoxPowerTestCase, self).setUp()
driver_info = INFO_DICT.copy()
mgr_utils.mock_the_extension_manager(driver="fake_vbox")
self.node = obj_utils.create_test_node(self.context,
driver='fake_vbox',
driver_info=driver_info)
def test_get_properties(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
properties = task.driver.power.get_properties()
self.assertIn('virtualbox_vmname', properties)
self.assertIn('virtualbox_host', properties)
@mock.patch.object(virtualbox, '_parse_driver_info', autospec=True)
def test_validate(self, parse_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.power.validate(task)
parse_info_mock.assert_called_once_with(task.node)
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_get_power_state(self, run_method_mock):
run_method_mock.return_value = 'PoweredOff'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
power_state = task.driver.power.get_power_state(task)
run_method_mock.assert_called_once_with(task.node,
'get_power_state',
'get_power_status')
self.assertEqual(states.POWER_OFF, power_state)
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_get_power_state_invalid_state(self, run_method_mock):
run_method_mock.return_value = 'invalid-state'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
power_state = task.driver.power.get_power_state(task)
run_method_mock.assert_called_once_with(task.node,
'get_power_state',
'get_power_status')
self.assertEqual(states.ERROR, power_state)
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_power_state_off(self, run_method_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.power.set_power_state(task, states.POWER_OFF)
run_method_mock.assert_called_once_with(task.node,
'set_power_state',
'stop')
@mock.patch.object(virtualbox.VirtualBoxManagement, 'set_boot_device')
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_power_state_on(self, run_method_mock, set_boot_device_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_internal_info['vbox_target_boot_device'] = 'pxe'
task.driver.power.set_power_state(task, states.POWER_ON)
run_method_mock.assert_called_once_with(task.node,
'set_power_state',
'start')
set_boot_device_mock.assert_called_once_with(task, 'pxe')
@mock.patch.object(virtualbox.VirtualBoxManagement, 'set_boot_device')
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_power_state_reboot(self, run_method_mock,
mock_set_boot_device):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_internal_info['vbox_target_boot_device'] = 'pxe'
task.driver.power.set_power_state(task, states.REBOOT)
run_method_mock.assert_any_call(task.node,
'reboot',
'stop')
mock_set_boot_device.assert_called_once_with(task, 'pxe')
run_method_mock.assert_any_call(task.node,
'reboot',
'start')
def test_set_power_state_invalid_state(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.power.set_power_state,
task, 'invalid-state')
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_reboot(self, run_method_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.power.reboot(task)
run_method_mock.assert_any_call(task.node,
'reboot',
'stop')
run_method_mock.assert_any_call(task.node,
'reboot',
'start')
class VirtualBoxManagementTestCase(db_base.DbTestCase):
def setUp(self):
super(VirtualBoxManagementTestCase, self).setUp()
driver_info = INFO_DICT.copy()
mgr_utils.mock_the_extension_manager(driver="fake_vbox")
self.node = obj_utils.create_test_node(self.context,
driver='fake_vbox',
driver_info=driver_info)
def test_get_properties(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
properties = task.driver.management.get_properties()
self.assertIn('virtualbox_vmname', properties)
self.assertIn('virtualbox_host', properties)
@mock.patch.object(virtualbox, '_parse_driver_info', autospec=True)
def test_validate(self, parse_info_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.management.validate(task)
parse_info_mock.assert_called_once_with(task.node)
def test_get_supported_boot_devices(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
devices = task.driver.management.get_supported_boot_devices(task)
self.assertIn(boot_devices.PXE, devices)
self.assertIn(boot_devices.DISK, devices)
self.assertIn(boot_devices.CDROM, devices)
@mock.patch.object(virtualbox.VirtualBoxPower,
'get_power_state', autospec=True)
@mock.patch.object(virtualbox, '_run_virtualbox_method',
autospec=True)
def test_get_boot_device_VM_Poweroff_ok(self, run_method_mock,
get_power_state_mock):
run_method_mock.return_value = 'Network'
get_power_state_mock.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ret_val = task.driver.management.get_boot_device(task)
run_method_mock.assert_called_once_with(task.node,
'get_boot_device',
'get_boot_device')
self.assertEqual(boot_devices.PXE, ret_val['boot_device'])
self.assertTrue(ret_val['persistent'])
@mock.patch.object(virtualbox.VirtualBoxPower,
'get_power_state', autospec=True)
def test_get_boot_device_VM_Poweron_ok(self, get_power_state_mock):
get_power_state_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_internal_info['vbox_target_boot_device'] = 'pxe'
ret_val = task.driver.management.get_boot_device(task)
self.assertEqual(boot_devices.PXE, ret_val['boot_device'])
self.assertTrue(ret_val['persistent'])
@mock.patch.object(virtualbox.VirtualBoxPower,
'get_power_state', autospec=True)
@mock.patch.object(virtualbox, '_run_virtualbox_method',
autospec=True)
def test_get_boot_device_target_device_none_ok(self,
run_method_mock,
get_power_state_mock):
run_method_mock.return_value = 'Network'
get_power_state_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_internal_info['vbox_target_boot_device'] = None
ret_val = task.driver.management.get_boot_device(task)
run_method_mock.assert_called_once_with(task.node,
'get_boot_device',
'get_boot_device')
self.assertEqual(boot_devices.PXE, ret_val['boot_device'])
self.assertTrue(ret_val['persistent'])
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_get_boot_device_invalid(self, run_method_mock):
run_method_mock.return_value = 'invalid-boot-device'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ret_val = task.driver.management.get_boot_device(task)
self.assertIsNone(ret_val['boot_device'])
self.assertIsNone(ret_val['persistent'])
@mock.patch.object(virtualbox.VirtualBoxPower,
'get_power_state', autospec=True)
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_boot_device_VM_Poweroff_ok(self, run_method_mock,
get_power_state_mock):
get_power_state_mock.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.management.set_boot_device(task, boot_devices.PXE)
run_method_mock.assert_called_with(task.node,
'set_boot_device',
'set_boot_device',
'Network')
@mock.patch.object(virtualbox.VirtualBoxPower,
'get_power_state', autospec=True)
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_boot_device_VM_Poweron_ok(self, run_method_mock,
get_power_state_mock):
get_power_state_mock.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.management.set_boot_device(task, boot_devices.PXE)
self.assertEqual('pxe',
task.node.driver_internal_info
['vbox_target_boot_device'])
@mock.patch.object(virtualbox, '_run_virtualbox_method', autospec=True)
def test_set_boot_device_invalid(self, run_method_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.set_boot_device,
task, 'invalid-boot-device')
def test_get_sensors_data(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(NotImplementedError,
task.driver.management.get_sensors_data,
task)

View File

@ -33,15 +33,11 @@ from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules import iscsi_deploy
from ironic.drivers.modules.msftocs import management as msftocs_management
from ironic.drivers.modules.msftocs import power as msftocs_power
from ironic.drivers.modules import pxe as pxe_module
from ironic.drivers.modules import seamicro
from ironic.drivers.modules import snmp
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 import pxe
@ -83,28 +79,6 @@ class PXEDriversTestCase(testtools.TestCase):
self.assertRaises(exception.DriverLoadError,
pxe.PXEAndIPMINativeDriver)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_seamicro_driver(self, try_import_mock):
try_import_mock.return_value = True
driver = pxe.PXEAndSeaMicroDriver()
self.assertIsInstance(driver.power, seamicro.Power)
self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
self.assertIsInstance(driver.management, seamicro.Management)
self.assertIsInstance(driver.vendor, seamicro.VendorPassthru)
self.assertIsInstance(driver.console, seamicro.ShellinaboxConsole)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_seamicro_driver_import_error(self, try_import_mock):
try_import_mock.return_value = False
self.assertRaises(exception.DriverLoadError,
pxe.PXEAndSeaMicroDriver)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_ilo_driver(self, try_import_mock):
@ -173,41 +147,6 @@ class PXEDriversTestCase(testtools.TestCase):
self.assertRaises(exception.DriverLoadError,
pxe.PXEAndIRMCDriver)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_vbox_driver(self, try_import_mock):
try_import_mock.return_value = True
driver = pxe.PXEAndVirtualBoxDriver()
self.assertIsInstance(driver.power, virtualbox.VirtualBoxPower)
self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
self.assertIsInstance(driver.management,
virtualbox.VirtualBoxManagement)
self.assertIsInstance(driver.raid, agent.AgentRAID)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_vbox_driver_import_error(self, try_import_mock):
try_import_mock.return_value = False
self.assertRaises(exception.DriverLoadError,
pxe.PXEAndVirtualBoxDriver)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_msftocs_driver(self, try_import_mock):
try_import_mock.return_value = True
driver = pxe.PXEAndMSFTOCSDriver()
self.assertIsInstance(driver.power, msftocs_power.MSFTOCSPower)
self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
self.assertIsInstance(driver.management,
msftocs_management.MSFTOCSManagement)
@mock.patch.object(pxe.importutils, 'try_import', spec_set=True,
autospec=True)
def test_pxe_ucs_driver(self, try_import_mock):

View File

@ -73,19 +73,6 @@ PYGHMI_IPMICMD_SPEC = (
'Command',
)
# pyremotevbox
PYREMOTEVBOX_SPEC = (
'exception',
'vbox',
)
PYREMOTEVBOX_EXC_SPEC = (
'PyRemoteVBoxException',
'VmInWrongPowerState',
)
PYREMOTEVBOX_VBOX_SPEC = (
'VirtualBoxHost',
)
# pywsman
PYWSMAN_SPEC = (
'Client',
@ -150,17 +137,3 @@ ONEVIEWCLIENT_STATES_SPEC = (
'ONEVIEW_RESETTING',
'ONEVIEW_ERROR',
)
# seamicro
SEAMICRO_SPEC = (
'client',
'exceptions',
)
# seamicro.client module
SEAMICRO_CLIENT_MOD_SPEC = (
'Client',
)
SEAMICRO_EXC_SPEC = (
'ClientException',
'UnsupportedVersion',
)

View File

@ -22,7 +22,6 @@ respective external libraries' actually being present.
Any external library required by a third-party driver should be mocked here.
Current list of mocked libraries:
- seamicroclient
- ipminative
- proliantutils
- pysnmp
@ -43,24 +42,6 @@ from ironic.tests.unit.drivers import third_party_driver_mock_specs \
as mock_specs
# attempt to load the external 'seamicroclient' library, which is
# required by the optional drivers.modules.seamicro module
seamicroclient = importutils.try_import("seamicroclient")
if not seamicroclient:
smc = mock.MagicMock(spec_set=mock_specs.SEAMICRO_SPEC)
smc.client = mock.MagicMock(spec_set=mock_specs.SEAMICRO_CLIENT_MOD_SPEC)
smc.exceptions = mock.MagicMock(spec_set=mock_specs.SEAMICRO_EXC_SPEC)
smc.exceptions.ClientException = Exception
smc.exceptions.UnsupportedVersion = Exception
sys.modules['seamicroclient'] = smc
sys.modules['seamicroclient.client'] = smc.client
sys.modules['seamicroclient.exceptions'] = smc.exceptions
# if anything has loaded the seamicro driver yet, reload it now that
# the external library has been mocked
if 'ironic.drivers.modules.seamicro' in sys.modules:
six.moves.reload_module(sys.modules['ironic.drivers.modules.seamicro'])
# IPMITool driver checks the system for presence of 'ipmitool' binary during
# __init__. We bypass that check in order to run the unit tests, which do not
# depend on 'ipmitool' being on the system.
@ -217,21 +198,6 @@ irmc_boot.check_share_fs_mounted_patcher = mock.patch(
irmc_boot.check_share_fs_mounted_patcher.return_value = None
pyremotevbox = importutils.try_import('pyremotevbox')
if not pyremotevbox:
pyremotevbox = mock.MagicMock(spec_set=mock_specs.PYREMOTEVBOX_SPEC)
pyremotevbox.exception = mock.MagicMock(
spec_set=mock_specs.PYREMOTEVBOX_EXC_SPEC)
pyremotevbox.exception.PyRemoteVBoxException = Exception
pyremotevbox.exception.VmInWrongPowerState = Exception
pyremotevbox.vbox = mock.MagicMock(
spec_set=mock_specs.PYREMOTEVBOX_VBOX_SPEC)
sys.modules['pyremotevbox'] = pyremotevbox
if 'ironic.drivers.modules.virtualbox' in sys.modules:
six.moves.reload_module(
sys.modules['ironic.drivers.modules.virtualbox'])
ironic_inspector_client = importutils.try_import('ironic_inspector_client')
if not ironic_inspector_client:
ironic_inspector_client = mock.MagicMock(

View File

@ -0,0 +1,32 @@
---
upgrade:
- |
A number of drivers that were declared as unsupported in Newton release
have been removed from ironic tree. This includes drivers with
power and/or management driver interfaces based on:
- MSFT OCS
- SeaMicro client
- Virtualbox over pyremotevbox client
As a result, the following ironic drivers will no longer be available:
- agent_vbox
- fake_msftocs
- fake_seamicro
- fake_vbox
- pxe_msftocs
- pxe_seamicro
- pxe_vbox
After upgrading, if one or more of these drivers are in the
'enabled_drivers' configuration option,
the ironic-conductor service will fail to start.
Any existing ironic nodes with these drivers assigned will become
inoperational via ironic after ironic upgrade,
as it will be not possible to change any node state/properties
except changing the node driver.
Operators having one of the drivers listed above enabled are required to
either disable those drivers and assign another existing driver
to affected nodes as appropriate,
or install these drivers from elsewhere separately.

View File

@ -49,7 +49,6 @@ ironic.drivers =
agent_pxe_oneview = ironic.drivers.oneview:AgentPXEOneViewDriver
agent_pyghmi = ironic.drivers.agent:AgentAndIPMINativeDriver
agent_ssh = ironic.drivers.agent:AgentAndSSHDriver
agent_vbox = ironic.drivers.agent:AgentAndVirtualBoxDriver
agent_ucs = ironic.drivers.agent:AgentAndUcsDriver
fake = ironic.drivers.fake:FakeDriver
fake_soft_power = ironic.drivers.fake:FakeSoftPowerDriver
@ -60,13 +59,10 @@ ironic.drivers =
fake_ipminative = ironic.drivers.fake:FakeIPMINativeDriver
fake_ssh = ironic.drivers.fake:FakeSSHDriver
fake_pxe = ironic.drivers.fake:FakePXEDriver
fake_seamicro = ironic.drivers.fake:FakeSeaMicroDriver
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_msftocs = ironic.drivers.fake:FakeMSFTOCSDriver
fake_ucs = ironic.drivers.fake:FakeUcsDriver
fake_cimc = ironic.drivers.fake:FakeCIMCDriver
fake_oneview = ironic.drivers.fake:FakeOneViewDriver
@ -77,14 +73,11 @@ ironic.drivers =
pxe_ipmitool_socat = ironic.drivers.ipmi:PXEAndIPMIToolAndSocatDriver
pxe_ipminative = ironic.drivers.pxe:PXEAndIPMINativeDriver
pxe_ssh = ironic.drivers.pxe:PXEAndSSHDriver
pxe_vbox = ironic.drivers.pxe:PXEAndVirtualBoxDriver
pxe_seamicro = ironic.drivers.pxe:PXEAndSeaMicroDriver
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_msftocs = ironic.drivers.pxe:PXEAndMSFTOCSDriver
pxe_ucs = ironic.drivers.pxe:PXEAndUcsDriver
pxe_iscsi_cimc = ironic.drivers.pxe:PXEAndCIMCDriver
pxe_agent_cimc = ironic.drivers.agent:AgentAndCIMCDriver