diff options
Diffstat (limited to 'ironic/drivers')
-rw-r--r-- | ironic/drivers/agent.py | 28 | ||||
-rw-r--r-- | ironic/drivers/fake.py | 47 | ||||
-rw-r--r-- | ironic/drivers/modules/msftocs/__init__.py | 0 | ||||
-rw-r--r-- | ironic/drivers/modules/msftocs/common.py | 110 | ||||
-rw-r--r-- | ironic/drivers/modules/msftocs/management.py | 121 | ||||
-rw-r--r-- | ironic/drivers/modules/msftocs/msftocsclient.py | 176 | ||||
-rw-r--r-- | ironic/drivers/modules/msftocs/power.py | 105 | ||||
-rw-r--r-- | ironic/drivers/modules/seamicro.py | 672 | ||||
-rw-r--r-- | ironic/drivers/modules/virtualbox.py | 393 | ||||
-rw-r--r-- | ironic/drivers/pxe.py | 76 |
10 files changed, 0 insertions, 1728 deletions
diff --git a/ironic/drivers/agent.py b/ironic/drivers/agent.py index 44065a971..c4c36f02c 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 be838e975..e926aaa8f 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 e69de29bb..000000000 --- a/ironic/drivers/modules/msftocs/__init__.py +++ /dev/null diff --git a/ironic/drivers/modules/msftocs/common.py b/ironic/drivers/modules/msftocs/common.py deleted file mode 100644 index 97d069967..000000000 --- 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 f148c4394..000000000 --- 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 d511c68ad..000000000 --- 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 e5ce62066..000000000 --- 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 cf45bb6d0..000000000 --- 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 <int>/<int>")) - - url = urlparse.urlparse(api_endpoint) - if (not (url.scheme == "http") or not url.netloc): - raise exception.InvalidParameterValue(_( - "Invalid 'seamicro_api_endpoint' parameter in node's " - "driver_info.")) - - res = {'username': username, - 'password': password, - 'api_endpoint': api_endpoint, - 'server_id': server_id, - 'api_version': api_version, - 'uuid': node.uuid, - 'port': port} - - return res - - -def _get_server(driver_info): - """Get server from server_id.""" - - s_client = _get_client(**driver_info) - return s_client.servers.get(driver_info['server_id']) - - -def _get_volume(driver_info, volume_id): - """Get volume from volume_id.""" - - s_client = _get_client(**driver_info) - return s_client.volumes.get(volume_id) - - -def _get_power_status(node): - """Get current power state of this node - - :param node: Ironic node one of :class:`ironic.db.models.Node` - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue if required seamicro parameters are - missing. - :raises: ServiceUnavailable on an error from SeaMicro Client. - :returns: Power state of the given node - """ - - seamicro_info = _parse_driver_info(node) - try: - server = _get_server(seamicro_info) - if not hasattr(server, 'active') or server.active is None: - return states.ERROR - if not server.active: - return states.POWER_OFF - elif server.active: - return states.POWER_ON - - except seamicro_client_exception.NotFound: - raise exception.NodeNotFound(node=node.uuid) - except seamicro_client_exception.ClientException as ex: - LOG.error(_LE("SeaMicro client exception %(msg)s for node %(uuid)s"), - {'msg': ex.message, 'uuid': node.uuid}) - raise exception.ServiceUnavailable(message=ex.message) - - -def _power_on(node, timeout=None): - """Power ON this node - - :param node: An Ironic node object. - :param timeout: Time in seconds to wait till power on is complete. - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue if required seamicro parameters are - missing. - :returns: Power state of the given node. - """ - if timeout is None: - timeout = CONF.seamicro.action_timeout - state = [None] - retries = [0] - seamicro_info = _parse_driver_info(node) - server = _get_server(seamicro_info) - - def _wait_for_power_on(state, retries): - """Called at an interval until the node is powered on.""" - - state[0] = _get_power_status(node) - if state[0] == states.POWER_ON: - raise loopingcall.LoopingCallDone() - - if retries[0] > CONF.seamicro.max_retry: - state[0] = states.ERROR - raise loopingcall.LoopingCallDone() - try: - retries[0] += 1 - server.power_on() - except seamicro_client_exception.ClientException: - LOG.warning(_LW("Power-on failed for node %s."), - node.uuid) - - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_on, - state, retries) - timer.start(interval=timeout).wait() - return state[0] - - -def _power_off(node, timeout=None): - """Power OFF this node - - :param node: Ironic node one of :class:`ironic.db.models.Node` - :param timeout: Time in seconds to wait till power off is compelete - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue if required seamicro parameters are - missing. - :returns: Power state of the given node - """ - if timeout is None: - timeout = CONF.seamicro.action_timeout - state = [None] - retries = [0] - seamicro_info = _parse_driver_info(node) - server = _get_server(seamicro_info) - - def _wait_for_power_off(state, retries): - """Called at an interval until the node is powered off.""" - - state[0] = _get_power_status(node) - if state[0] == states.POWER_OFF: - raise loopingcall.LoopingCallDone() - - if retries[0] > CONF.seamicro.max_retry: - state[0] = states.ERROR - raise loopingcall.LoopingCallDone() - try: - retries[0] += 1 - server.power_off() - except seamicro_client_exception.ClientException: - LOG.warning(_LW("Power-off failed for node %s."), - node.uuid) - - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_off, - state, retries) - timer.start(interval=timeout).wait() - return state[0] - - -def _reboot(node, timeout=None): - """Reboot this node. - - :param node: Ironic node one of :class:`ironic.db.models.Node` - :param timeout: Time in seconds to wait till reboot is compelete - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue if required seamicro parameters are - missing. - :returns: Power state of the given node - """ - if timeout is None: - timeout = CONF.seamicro.action_timeout - state = [None] - retries = [0] - seamicro_info = _parse_driver_info(node) - server = _get_server(seamicro_info) - - def _wait_for_reboot(state, retries): - """Called at an interval until the node is rebooted successfully.""" - - state[0] = _get_power_status(node) - if state[0] == states.POWER_ON: - raise loopingcall.LoopingCallDone() - - if retries[0] > CONF.seamicro.max_retry: - state[0] = states.ERROR - raise loopingcall.LoopingCallDone() - - try: - retries[0] += 1 - server.reset() - except seamicro_client_exception.ClientException: - LOG.warning(_LW("Reboot failed for node %s."), - node.uuid) - - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot, - state, retries) - server.reset() - timer.start(interval=timeout).wait() - return state[0] - - -def _validate_volume(driver_info, volume_id): - """Validates if volume is in Storage pools designated for ironic.""" - - volume = _get_volume(driver_info, volume_id) - - # Check if the ironic <scard>/ironic-<pool_id>/<volume_id> naming scheme - # is present in volume id - try: - pool_id = volume.id.split('/')[1].lower() - except IndexError: - pool_id = "" - - if "ironic-" in pool_id: - return True - else: - raise exception.InvalidParameterValue(_( - "Invalid volume id specified")) - - -def _get_pools(driver_info, filters=None): - """Get SeaMicro storage pools matching given filters.""" - - s_client = _get_client(**driver_info) - return s_client.pools.list(filters=filters) - - -def _create_volume(driver_info, volume_size): - """Create volume in the SeaMicro storage pools designated for ironic.""" - - ironic_pools = _get_pools(driver_info, filters={'id': 'ironic-'}) - if ironic_pools is None: - raise exception.VendorPassthruException(_( - "No storage pools found for ironic")) - - least_used_pool = sorted(ironic_pools, - key=lambda x: x.freeSize)[0] - return _get_client(**driver_info).volumes.create(volume_size, - least_used_pool) - - -def get_telnet_port(driver_info): - """Get SeaMicro telnet port to listen.""" - server_id = int(driver_info['server_id'].split("/")[0]) - return PORT_BASE + (10 * server_id) - - -class Power(base.PowerInterface): - """SeaMicro Power Interface. - - This PowerInterface class provides a mechanism for controlling the power - state of servers in a seamicro chassis. - """ - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task): - """Check that node 'driver_info' is valid. - - Check that node 'driver_info' contains the required fields. - - :param task: a TaskManager instance containing the node to act on. - :raises: MissingParameterValue if required seamicro parameters are - missing. - """ - _parse_driver_info(task.node) - - def get_power_state(self, task): - """Get the current power state of the task's node. - - Poll the host for the current power state of the node. - - :param task: a TaskManager instance containing the node to act on. - :raises: ServiceUnavailable on an error from SeaMicro Client. - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue when a required parameter is missing - :returns: power state. One of :class:`ironic.common.states`. - - """ - return _get_power_status(task.node) - - @task_manager.require_exclusive_lock - def set_power_state(self, task, pstate): - """Turn the power on or off. - - Set the power state of a node. - - :param task: a TaskManager instance containing the node to act on. - :param pstate: Either POWER_ON or POWER_OFF from :class: - `ironic.common.states`. - :raises: InvalidParameterValue if an invalid power state was specified - or a seamicro parameter is invalid. - :raises: MissingParameterValue when a required parameter is missing - :raises: PowerStateFailure if the desired power state couldn't be set. - """ - - if pstate == states.POWER_ON: - state = _power_on(task.node) - elif pstate == states.POWER_OFF: - state = _power_off(task.node) - else: - raise exception.InvalidParameterValue(_( - "set_power_state called with invalid power state.")) - - if state != pstate: - raise exception.PowerStateFailure(pstate=pstate) - - @task_manager.require_exclusive_lock - def reboot(self, task): - """Cycles the power to the task's node. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if a seamicro parameter is invalid. - :raises: MissingParameterValue if required seamicro parameters are - missing. - :raises: PowerStateFailure if the final state of the node is not - POWER_ON. - """ - state = _reboot(task.node) - - if state != states.POWER_ON: - raise exception.PowerStateFailure(pstate=states.POWER_ON) - - -class VendorPassthru(base.VendorInterface): - """SeaMicro vendor-specific methods.""" - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task, method, **kwargs): - _parse_driver_info(task.node) - - @base.passthru(['POST'], - description=_("Set an untagged VLAN ID for NIC 0 of node. " - "Required argument: 'vlan_id' - ID of " - "untagged VLAN.")) - def set_node_vlan_id(self, task, **kwargs): - """Sets an untagged vlan id for NIC 0 of node. - - @kwargs vlan_id: id of untagged vlan for NIC 0 of node - """ - node = task.node - vlan_id = kwargs.get('vlan_id') - if not vlan_id: - raise exception.MissingParameterValue(_("No vlan id provided")) - - seamicro_info = _parse_driver_info(node) - try: - server = _get_server(seamicro_info) - - # remove current vlan for server - if len(server.nic['0']['untaggedVlan']) > 0: - server.unset_untagged_vlan(server.nic['0']['untaggedVlan']) - server = server.refresh(5) - server.set_untagged_vlan(vlan_id) - except seamicro_client_exception.ClientException as ex: - LOG.error(_LE("SeaMicro client exception: %s"), ex.message) - raise exception.VendorPassthruException(message=ex.message) - - properties = node.properties - properties['seamicro_vlan_id'] = vlan_id - node.properties = properties - node.save() - - @base.passthru(['POST'], - description=_("Attach volume to node. Arguments: " - "1. 'volume_id' - ID of pre-provisioned " - "volume. This is optional. If not specified, " - "a volume is created in SeaMicro storage " - "pool. 2. 'volume_size' - size of new volume " - "(if volume_id is not specified).")) - def attach_volume(self, task, **kwargs): - """Attach a volume to a node. - - Attach volume from SeaMicro storage pools for ironic to node. - If kwargs['volume_id'] not given, Create volume in SeaMicro - storage pool and attach to node. - - @kwargs volume_id: id of pre-provisioned volume that is to be attached - as root volume of node - @kwargs volume_size: size of new volume to be created and attached - as root volume of node - """ - node = task.node - seamicro_info = _parse_driver_info(node) - volume_id = kwargs.get('volume_id') - - if volume_id is None: - volume_size = kwargs.get('volume_size') - if volume_size is None: - raise exception.MissingParameterValue( - _("No volume size provided for creating volume")) - volume_id = _create_volume(seamicro_info, volume_size) - - if _validate_volume(seamicro_info, volume_id): - try: - server = _get_server(seamicro_info) - server.detach_volume() - server = server.refresh(5) - server.attach_volume(volume_id) - except seamicro_client_exception.ClientException as ex: - LOG.error(_LE("SeaMicro client exception: %s"), ex.message) - raise exception.VendorPassthruException(message=ex.message) - - properties = node.properties - properties['seamicro_volume_id'] = volume_id - node.properties = properties - node.save() - - -class Management(base.ManagementInterface): - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task): - """Check that 'driver_info' contains SeaMicro credentials. - - Validates whether the 'driver_info' property of the supplied - task's node contains the required credentials information. - - :param task: a task from TaskManager. - :raises: MissingParameterValue when a required parameter is missing - - """ - _parse_driver_info(task.node) - - def get_supported_boot_devices(self, task): - """Get a list of the supported boot devices. - - :param task: a task from TaskManager. - :returns: A list with the supported boot devices defined - in :mod:`ironic.common.boot_devices`. - - """ - return list(_BOOT_DEVICES_MAP.keys()) - - @task_manager.require_exclusive_lock - def set_boot_device(self, task, device, persistent=False): - """Set the boot device for the task's node. - - Set the boot device to use on next reboot of the node. - - :param task: a task from TaskManager. - :param device: the boot device, one of - :mod:`ironic.common.boot_devices`. - :param persistent: Boolean value. True if the boot device will - persist to all future boots, False if not. - Default: False. Ignored by this driver. - :raises: InvalidParameterValue if an invalid boot device is - specified or if a seamicro parameter is invalid. - :raises: IronicException on an error from seamicro-client. - :raises: MissingParameterValue when a required parameter is missing - - """ - if device not in self.get_supported_boot_devices(task): - raise exception.InvalidParameterValue(_( - "Invalid boot device %s specified.") % device) - - seamicro_info = _parse_driver_info(task.node) - try: - server = _get_server(seamicro_info) - boot_device = _BOOT_DEVICES_MAP[device] - server.set_boot_order(boot_device) - except seamicro_client_exception.ClientException as ex: - LOG.error(_LE("Seamicro set boot device failed for node " - "%(node)s with the following error: %(error)s"), - {'node': task.node.uuid, 'error': ex}) - raise exception.IronicException(ex) - - def get_boot_device(self, task): - """Get the current boot device for the task's node. - - Returns the current boot device of the node. Be aware that not - all drivers support this. - - :param task: a task from TaskManager. - :returns: a dictionary containing: - - :boot_device: the boot device, one of - :mod:`ironic.common.boot_devices` or None if it is unknown. - :persistent: Whether the boot device will persist to all - future boots or not, None if it is unknown. - - """ - # TODO(lucasagomes): The python-seamicroclient library currently - # doesn't expose a method to get the boot device, update it once - # it's implemented. - return {'boot_device': None, 'persistent': None} - - def get_sensors_data(self, task): - """Get sensors data method. - - Not implemented by this driver. - :param task: a TaskManager instance. - - """ - raise NotImplementedError() - - -class ShellinaboxConsole(base.ConsoleInterface): - """A ConsoleInterface that uses telnet and shellinabox.""" - - def get_properties(self): - d = COMMON_PROPERTIES.copy() - d.update(CONSOLE_PROPERTIES) - return d - - def validate(self, task): - """Validate the Node console info. - - :param task: a task from TaskManager. - :raises: MissingParameterValue if required seamicro parameters are - missing - :raises: InvalidParameterValue if required parameter are invalid. - """ - driver_info = _parse_driver_info(task.node) - if not driver_info['port']: - raise exception.MissingParameterValue(_( - "Missing 'seamicro_terminal_port' parameter in node's " - "driver_info")) - - def start_console(self, task): - """Start a remote console for the node. - - :param task: a task from TaskManager - :raises: MissingParameterValue if required seamicro parameters are - missing - :raises: ConsoleError if the directory for the PID file cannot be - created - :raises: ConsoleSubprocessFailed when invoking the subprocess failed - :raises: InvalidParameterValue if required parameter are invalid. - """ - - driver_info = _parse_driver_info(task.node) - telnet_port = get_telnet_port(driver_info) - chassis_ip = urlparse.urlparse(driver_info['api_endpoint']).netloc - - seamicro_cmd = ("/:%(uid)s:%(gid)s:HOME:telnet %(chassis)s %(port)s" - % {'uid': os.getuid(), - 'gid': os.getgid(), - 'chassis': chassis_ip, - 'port': telnet_port}) - - console_utils.start_shellinabox_console(driver_info['uuid'], - driver_info['port'], - seamicro_cmd) - - def stop_console(self, task): - """Stop the remote console session for the node. - - :param task: a task from TaskManager - :raises: ConsoleError if unable to stop the console - """ - - console_utils.stop_shellinabox_console(task.node.uuid) - - def get_console(self, task): - """Get the type and connection information about the console. - - :raises: MissingParameterValue if required seamicro parameters are - missing - :raises: InvalidParameterValue if required parameter are invalid. - """ - - driver_info = _parse_driver_info(task.node) - url = console_utils.get_shellinabox_console_url(driver_info['port']) - return {'type': 'shellinabox', 'url': url} diff --git a/ironic/drivers/modules/virtualbox.py b/ironic/drivers/modules/virtualbox.py deleted file mode 100644 index c3eda6a4c..000000000 --- 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 8f890f974..8f93170c0 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. |