diff --git a/doc/source/deploy/console.rst b/doc/source/deploy/console.rst index 55d12ba168..8be1ea669d 100644 --- a/doc/source/deploy/console.rst +++ b/doc/source/deploy/console.rst @@ -125,9 +125,8 @@ The web console can be configured in Bare Metal service in the following way: ironic driver-properties 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 ````, for example ``8023``, this customized port is used in - web console url. + Give a customized port number to ````, + for example ``8023``, this customized port is used in web console url. Get web console information for a node as follows:: diff --git a/doc/source/deploy/drivers.rst b/doc/source/deploy/drivers.rst index 55bf35f56d..56a286a6ce 100644 --- a/doc/source/deploy/drivers.rst +++ b/doc/source/deploy/drivers.rst @@ -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 diff --git a/doc/source/drivers/seamicro.rst b/doc/source/drivers/seamicro.rst deleted file mode 100644 index c475278d91..0000000000 --- a/doc/source/drivers/seamicro.rst +++ /dev/null @@ -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:///v2.0 -- ``seamicro_server_id``: SeaMicro Server ID. Expected format is / -- ``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 - - Create Node:: - - ironic node-create -d pxe_seamicro -i seamicro_api_endpoint=https:/// -i seamicro_server_id= -i seamicro_username= -i seamicro_password= -i seamicro_api_version= -i seamicro_terminal_port= -i deploy_kernel= -i deploy_ramdisk= -p cpus= -p memory_mb= -p local_gb= -p cpu_arch= - - Associate port with the node created:: - - ironic port-create -n $NODE -a - - Associate properties with the flavor:: - - nova flavor-key baremetal set "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 diff --git a/doc/source/drivers/vbox.rst b/doc/source/drivers/vbox.rst deleted file mode 100644 index 0380478d44..0000000000 --- a/doc/source/drivers/vbox.rst +++ /dev/null @@ -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= -i virtualbox_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 diff --git a/driver-requirements.txt b/driver-requirements.txt index 1c38fb1951..af57659d02 100644 --- a/driver-requirements.txt +++ b/driver-requirements.txt @@ -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 diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index 13f44e9db5..d5e1196c27 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -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 = - - -[virtualbox] - -# -# From ironic -# - -# Port on which VirtualBox web service is listening. (port -# value) -# Minimum value: 0 -# Maximum value: 65535 -#port = 18083 diff --git a/install-guide/source/standalone.rst b/install-guide/source/standalone.rst index 6e83c0079b..0cbf17851e 100644 --- a/install-guide/source/standalone.rst +++ b/install-guide/source/standalone.rst @@ -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 diff --git a/ironic/common/exception.py b/ironic/common/exception.py index 347e029fbc..03bbd14af5 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -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") diff --git a/ironic/conf/__init__.py b/ironic/conf/__init__.py index 6456ddc6ce..9c2097c24e 100644 --- a/ironic/conf/__init__.py +++ b/ironic/conf/__init__.py @@ -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) diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py index b365037d00..3c9caf76d0 100644 --- a/ironic/conf/opts.py +++ b/ironic/conf/opts.py @@ -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), ] diff --git a/ironic/conf/seamicro.py b/ironic/conf/seamicro.py deleted file mode 100644 index 8211122459..0000000000 --- a/ironic/conf/seamicro.py +++ /dev/null @@ -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) diff --git a/ironic/conf/virtualbox.py b/ironic/conf/virtualbox.py deleted file mode 100644 index 24a6193381..0000000000 --- a/ironic/conf/virtualbox.py +++ /dev/null @@ -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') diff --git a/ironic/drivers/agent.py b/ironic/drivers/agent.py index 44065a9710..c4c36f02cd 100644 --- a/ironic/drivers/agent.py +++ b/ironic/drivers/agent.py @@ -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. diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py index be838e9751..e926aaa8f7 100644 --- a/ironic/drivers/fake.py +++ b/ironic/drivers/fake.py @@ -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.""" diff --git a/ironic/drivers/modules/msftocs/__init__.py b/ironic/drivers/modules/msftocs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ironic/drivers/modules/msftocs/common.py b/ironic/drivers/modules/msftocs/common.py deleted file mode 100644 index 97d069967b..0000000000 --- a/ironic/drivers/modules/msftocs/common.py +++ /dev/null @@ -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) diff --git a/ironic/drivers/modules/msftocs/management.py b/ironic/drivers/modules/msftocs/management.py deleted file mode 100644 index f148c43949..0000000000 --- a/ironic/drivers/modules/msftocs/management.py +++ /dev/null @@ -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() diff --git a/ironic/drivers/modules/msftocs/msftocsclient.py b/ironic/drivers/modules/msftocs/msftocsclient.py deleted file mode 100644 index d511c68ada..0000000000 --- a/ironic/drivers/modules/msftocs/msftocsclient.py +++ /dev/null @@ -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()})) diff --git a/ironic/drivers/modules/msftocs/power.py b/ironic/drivers/modules/msftocs/power.py deleted file mode 100644 index e5ce620667..0000000000 --- a/ironic/drivers/modules/msftocs/power.py +++ /dev/null @@ -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) diff --git a/ironic/drivers/modules/seamicro.py b/ironic/drivers/modules/seamicro.py deleted file mode 100644 index cf45bb6d05..0000000000 --- a/ironic/drivers/modules/seamicro.py +++ /dev/null @@ -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 /")) - - 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 /ironic-/ 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} diff --git a/ironic/drivers/modules/virtualbox.py b/ironic/drivers/modules/virtualbox.py deleted file mode 100644 index c3eda6a4cf..0000000000 --- a/ironic/drivers/modules/virtualbox.py +++ /dev/null @@ -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() diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py index 8f890f9740..8f93170c0e 100644 --- a/ironic/drivers/pxe.py +++ b/ironic/drivers/pxe.py @@ -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. diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py index 6714f1d023..1d2321c4e2 100644 --- a/ironic/tests/unit/conductor/test_manager.py +++ b/ironic/tests/unit/conductor/test_manager.py @@ -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', diff --git a/ironic/tests/unit/db/utils.py b/ironic/tests/unit/db/utils.py index bddbba6ec6..3c85969e99 100644 --- a/ironic/tests/unit/db/utils.py +++ b/ironic/tests/unit/db/utils.py @@ -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', diff --git a/ironic/tests/unit/drivers/modules/msftocs/__init__.py b/ironic/tests/unit/drivers/modules/msftocs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ironic/tests/unit/drivers/modules/msftocs/test_common.py b/ironic/tests/unit/drivers/modules/msftocs/test_common.py deleted file mode 100644 index 546748944b..0000000000 --- a/ironic/tests/unit/drivers/modules/msftocs/test_common.py +++ /dev/null @@ -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)) diff --git a/ironic/tests/unit/drivers/modules/msftocs/test_management.py b/ironic/tests/unit/drivers/modules/msftocs/test_management.py deleted file mode 100644 index aaa54a8795..0000000000 --- a/ironic/tests/unit/drivers/modules/msftocs/test_management.py +++ /dev/null @@ -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) diff --git a/ironic/tests/unit/drivers/modules/msftocs/test_msftocsclient.py b/ironic/tests/unit/drivers/modules/msftocs/test_msftocsclient.py deleted file mode 100644 index 66097d05e1..0000000000 --- a/ironic/tests/unit/drivers/modules/msftocs/test_msftocsclient.py +++ /dev/null @@ -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 = ( - '' - 'Success' - '1' - 'Success' - '1' - 'ForcePxe' - '') % msftocsclient.WCSNS - -FAKE_BLADE_RESPONSE = ( - '' - 'Success' - '1' - '' - '1' - '') % msftocsclient.WCSNS - -FAKE_POWER_STATE_RESPONSE = ( - '' - 'Success' - '1' - 'Blade Power is On, firmware decompressed' - '' - '1' - '0' - 'ON' - '') % msftocsclient.WCSNS - -FAKE_BLADE_STATE_RESPONSE = ( - '' - 'Success' - '1' - '' - '1' - 'ON' - '') % 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, - '' % msftocsclient.WCSNS) - - def test__check_completion_with_bad_completion_code_fail(self): - self.assertRaises(exception.MSFTOCSClientApiException, - self._client._check_completion_code, - '' - 'Fail' - '' % 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"}) diff --git a/ironic/tests/unit/drivers/modules/msftocs/test_power.py b/ironic/tests/unit/drivers/modules/msftocs/test_power.py deleted file mode 100644 index 38f07a4b97..0000000000 --- a/ironic/tests/unit/drivers/modules/msftocs/test_power.py +++ /dev/null @@ -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) diff --git a/ironic/tests/unit/drivers/modules/test_seamicro.py b/ironic/tests/unit/drivers/modules/test_seamicro.py deleted file mode 100644 index c6651aca10..0000000000 --- a/ironic/tests/unit/drivers/modules/test_seamicro.py +++ /dev/null @@ -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']) diff --git a/ironic/tests/unit/drivers/modules/test_virtualbox.py b/ironic/tests/unit/drivers/modules/test_virtualbox.py deleted file mode 100644 index 2e2d54adf5..0000000000 --- a/ironic/tests/unit/drivers/modules/test_virtualbox.py +++ /dev/null @@ -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) diff --git a/ironic/tests/unit/drivers/test_pxe.py b/ironic/tests/unit/drivers/test_pxe.py index c6799c9610..4464a440f5 100644 --- a/ironic/tests/unit/drivers/test_pxe.py +++ b/ironic/tests/unit/drivers/test_pxe.py @@ -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): diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index f82edba089..1fea414756 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -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', -) diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index 22aa8e76c8..5c1a373e6f 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -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( diff --git a/releasenotes/notes/remove-most-unsupported-049f3401c2554a3c.yaml b/releasenotes/notes/remove-most-unsupported-049f3401c2554a3c.yaml new file mode 100644 index 0000000000..744d422987 --- /dev/null +++ b/releasenotes/notes/remove-most-unsupported-049f3401c2554a3c.yaml @@ -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. diff --git a/setup.cfg b/setup.cfg index b872b2bf75..eba73c3535 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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