Merge "Remove deprecated xclarity hardware type"
This commit is contained in:
commit
8a209f1372
@ -842,11 +842,6 @@ function is_deployed_by_irmc {
|
||||
return 1
|
||||
}
|
||||
|
||||
function is_deployed_by_xclarity {
|
||||
[[ "$IRONIC_DEPLOY_DRIVER" == xclarity ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
function is_deployed_by_ibmc {
|
||||
[[ "$IRONIC_DEPLOY_DRIVER" == ibmc ]] && return 0
|
||||
return 1
|
||||
@ -2603,13 +2598,6 @@ function enroll_nodes {
|
||||
if [[ -n "$IRONIC_DEPLOY_ISO_ID" ]]; then
|
||||
node_options+=" --driver-info deploy_iso=$IRONIC_DEPLOY_ISO_ID"
|
||||
fi
|
||||
elif is_deployed_by_xclarity; then
|
||||
local xclarity_hardware_id
|
||||
xclarity_hardware_id=$(echo $hardware_info |awk '{print $5}')
|
||||
node_options+=" --driver-info xclarity_manager_ip=$bmc_address \
|
||||
--driver-info xclarity_password=$bmc_passwd \
|
||||
--driver-info xclarity_username=$bmc_username \
|
||||
--driver-info xclarity_hardware_id=$xclarity_hardware_id"
|
||||
elif is_deployed_by_ibmc; then
|
||||
node_options+=" --driver-info ibmc_address=$bmc_address \
|
||||
--driver-info ibmc_username=$bmc_username \
|
||||
|
@ -25,7 +25,6 @@ Hardware Types
|
||||
drivers/irmc
|
||||
drivers/redfish
|
||||
drivers/snmp
|
||||
drivers/xclarity
|
||||
drivers/fake
|
||||
|
||||
Changing Hardware Types and Interfaces
|
||||
|
@ -1,79 +0,0 @@
|
||||
===============
|
||||
XClarity driver
|
||||
===============
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
.. warning::
|
||||
The ``xclarity`` driver has been deprecated and is anticipated to be removed
|
||||
from Ironic at some point during or after the 2024.2 development cycle.
|
||||
The anticipated forward management path is to migrate to the ``redfish``
|
||||
hardware type.
|
||||
|
||||
The ``xclarity`` driver is targeted for IMM 2.0 and IMM 3.0 managed Lenovo
|
||||
servers. The xclarity hardware type enables the user to take advantage of
|
||||
`XClarity Manager`_ by using the `XClarity Python Client`_.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
* The XClarity Client library should be installed on the ironic conductor
|
||||
node(s).
|
||||
|
||||
For example, it can be installed with ``pip``::
|
||||
|
||||
sudo pip install python-xclarityclient
|
||||
|
||||
Enabling the XClarity driver
|
||||
============================
|
||||
|
||||
#. Add ``xclarity`` to the list of ``enabled_hardware_types``,
|
||||
``enabled_power_interfaces`` and ``enabled_management_interfaces``
|
||||
in ``/etc/ironic/ironic.conf``. For example::
|
||||
|
||||
[DEFAULT]
|
||||
...
|
||||
enabled_hardware_types = ipmi,xclarity
|
||||
enabled_power_interfaces = ipmitool,xclarity
|
||||
enabled_management_interfaces = ipmitool,xclarity
|
||||
|
||||
#. Restart the ironic conductor service::
|
||||
|
||||
sudo service ironic-conductor restart
|
||||
|
||||
# Or, for RDO:
|
||||
sudo systemctl restart openstack-ironic-conductor
|
||||
|
||||
Registering a node with the XClarity driver
|
||||
===========================================
|
||||
|
||||
Nodes configured to use the driver should have the ``driver`` property
|
||||
set to ``xclarity``.
|
||||
|
||||
The following properties are specified in the node's ``driver_info``
|
||||
field and are required:
|
||||
|
||||
- ``xclarity_manager_ip``: The IP address of the XClarity Controller.
|
||||
- ``xclarity_username``: User account with admin/server-profile access
|
||||
privilege to the XClarity Controller.
|
||||
- ``xclarity_password``: User account password corresponding to the
|
||||
xclarity_username to the XClarity Controller.
|
||||
- ``xclarity_hardware_id``: The hardware ID of the XClarity managed server.
|
||||
|
||||
The ``baremetal node create`` command can be used to enroll
|
||||
a node with the ``xclarity`` driver. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
baremetal node create --driver xclarity \
|
||||
--driver-info xclarity_manager_ip=https://10.240.217.101 \
|
||||
--driver-info xclarity_username=admin \
|
||||
--driver-info xclarity_password=password \
|
||||
--driver-info xclarity_hardware_id=hardware_id
|
||||
|
||||
For more information about enrolling nodes see :ref:`enrollment`
|
||||
in the install guide.
|
||||
|
||||
.. _`XClarity Manager`: http://www3.lenovo.com/us/en/data-center/software/systems-management/xclarity/
|
||||
.. _`XClarity Python Client`: http://pypi.org/project/python-xclarityclient/
|
@ -10,7 +10,6 @@ pyasn1>=0.5.1 # BSD
|
||||
pyasn1-modules>=0.3.0 # BSD
|
||||
python-scciclient>=0.16.0,<0.17.0
|
||||
python-dracclient>=5.1.0,<9.0.0
|
||||
python-xclarityclient>=0.1.6
|
||||
|
||||
# Ansible-deploy interface
|
||||
ansible>=2.7
|
||||
|
@ -651,10 +651,6 @@ class InstanceUnrescueFailure(IronicException):
|
||||
'%(node)s: %(reason)s')
|
||||
|
||||
|
||||
class XClarityError(IronicException):
|
||||
_msg_fmt = _("XClarity exception occurred. Error: %(error)s")
|
||||
|
||||
|
||||
class BIOSSettingAlreadyExists(Conflict):
|
||||
_msg_fmt = _('A BIOS setting %(name)s for node %(node)s already exists.')
|
||||
|
||||
|
@ -49,7 +49,6 @@ from ironic.conf import sensor_data
|
||||
from ironic.conf import service_catalog
|
||||
from ironic.conf import snmp
|
||||
from ironic.conf import swift
|
||||
from ironic.conf import xclarity
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -87,4 +86,3 @@ sensor_data.register_opts(CONF)
|
||||
service_catalog.register_opts(CONF)
|
||||
snmp.register_opts(CONF)
|
||||
swift.register_opts(CONF)
|
||||
xclarity.register_opts(CONF)
|
||||
|
@ -47,7 +47,6 @@ _opts = [
|
||||
('service_catalog', ironic.conf.service_catalog.list_opts()),
|
||||
('snmp', ironic.conf.snmp.opts),
|
||||
('swift', ironic.conf.swift.list_opts()),
|
||||
('xclarity', ironic.conf.xclarity.opts),
|
||||
]
|
||||
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2017 LENOVO Development Company, LP
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic.common.i18n import _
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('manager_ip',
|
||||
help=_('IP address of the XClarity Controller. '
|
||||
'Configuration here is deprecated and will be removed '
|
||||
'in the Stein release. Please update the driver_info '
|
||||
'field to use "xclarity_manager_ip" instead')),
|
||||
cfg.StrOpt('username',
|
||||
help=_('Username for the XClarity Controller. '
|
||||
'Configuration here is deprecated and will be removed '
|
||||
'in the Stein release. Please update the driver_info '
|
||||
'field to use "xclarity_username" instead')),
|
||||
cfg.StrOpt('password',
|
||||
secret=True,
|
||||
help=_('Password for XClarity Controller username. '
|
||||
'Configuration here is deprecated and will be removed '
|
||||
'in the Stein release. Please update the driver_info '
|
||||
'field to use "xclarity_password" instead')),
|
||||
cfg.PortOpt('port',
|
||||
default=443,
|
||||
help=_('Port to be used for XClarity Controller '
|
||||
'connection.')),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_opts(opts, group='xclarity')
|
@ -1,178 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
#
|
||||
# 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_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conf import CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
client = importutils.try_import('xclarity_client.client')
|
||||
xclarity_client_constants = importutils.try_import('xclarity_client.constants')
|
||||
xclarity_client_exceptions = importutils.try_import(
|
||||
'xclarity_client.exceptions')
|
||||
|
||||
REQUIRED_ON_DRIVER_INFO = {
|
||||
'xclarity_manager_ip': _("IP address of the XClarity Controller."),
|
||||
'xclarity_username': _("Username for the XClarity Controller "
|
||||
"with administrator privileges."),
|
||||
'xclarity_password': _("Password for xclarity_username."),
|
||||
'xclarity_hardware_id': _("Server Hardware ID managed by XClarity."),
|
||||
}
|
||||
|
||||
OPTIONAL_ON_DRIVER_INFO = {
|
||||
'xclarity_port': _("Port to be used for XClarity Controller connection. "
|
||||
"Optional"),
|
||||
}
|
||||
|
||||
COMMON_PROPERTIES = {}
|
||||
COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
||||
COMMON_PROPERTIES.update(OPTIONAL_ON_DRIVER_INFO)
|
||||
|
||||
|
||||
def get_properties():
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
|
||||
def parse_driver_info(node):
|
||||
"""Parse a node's driver_info values.
|
||||
|
||||
Parses the driver_info of the node, reads default values
|
||||
and returns a dict containing the combination of both.
|
||||
|
||||
:param node: an ironic node object to get information from.
|
||||
:returns: a dict containing information parsed from driver_info.
|
||||
:raises: InvalidParameterValue if some required information
|
||||
is missing on the node or inputs is invalid.
|
||||
"""
|
||||
driver_info = node.driver_info
|
||||
parsed_driver_info = {}
|
||||
|
||||
error_msgs = []
|
||||
for param in REQUIRED_ON_DRIVER_INFO:
|
||||
if param == "xclarity_hardware_id":
|
||||
try:
|
||||
parsed_driver_info[param] = str(driver_info[param])
|
||||
except KeyError:
|
||||
error_msgs.append(_("'%s' not provided to XClarity.") % param)
|
||||
except UnicodeEncodeError:
|
||||
error_msgs.append(_("'%s' contains non-ASCII symbol.") % param)
|
||||
else:
|
||||
# corresponding config names don't have 'xclarity_' prefix
|
||||
if param in driver_info:
|
||||
parsed_driver_info[param] = str(driver_info[param])
|
||||
elif param not in driver_info and\
|
||||
CONF.xclarity.get(param[len('xclarity_'):]) is not None:
|
||||
parsed_driver_info[param] = str(
|
||||
CONF.xclarity.get(param[len('xclarity_'):]))
|
||||
LOG.warning('The configuration [xclarity]/%(config)s '
|
||||
'is deprecated and will be removed in the '
|
||||
'Stein release. Please update the node '
|
||||
'%(node_uuid)s driver_info field to use '
|
||||
'"%(field)s" instead',
|
||||
{'config': param[len('xclarity_'):],
|
||||
'node_uuid': node.uuid, 'field': param})
|
||||
else:
|
||||
error_msgs.append(_("'%s' not provided to XClarity.") % param)
|
||||
|
||||
port = driver_info.get('xclarity_port', CONF.xclarity.get('port'))
|
||||
parsed_driver_info['xclarity_port'] = utils.validate_network_port(
|
||||
port, 'xclarity_port')
|
||||
|
||||
if error_msgs:
|
||||
msg = (_('The following errors were encountered while parsing '
|
||||
'driver_info:\n%s') % '\n'.join(error_msgs))
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return parsed_driver_info
|
||||
|
||||
|
||||
def get_xclarity_client(node):
|
||||
"""Generates an instance of the XClarity client.
|
||||
|
||||
Generates an instance of the XClarity client using the imported
|
||||
xclarity_client library.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:returns: an instance of the XClarity client
|
||||
:raises: XClarityError if can't get to the XClarity client
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
try:
|
||||
xclarity_client = client.Client(
|
||||
ip=driver_info.get('xclarity_manager_ip'),
|
||||
username=driver_info.get('xclarity_username'),
|
||||
password=driver_info.get('xclarity_password'),
|
||||
port=driver_info.get('xclarity_port')
|
||||
)
|
||||
except xclarity_client_exceptions.XClarityError as exc:
|
||||
msg = (_("Error getting connection to XClarity address: %(ip)s. "
|
||||
"Error: %(exc)s"),
|
||||
{'ip': driver_info.get('xclarity_manager_ip'), 'exc': exc})
|
||||
raise exception.XClarityError(error=msg)
|
||||
return xclarity_client
|
||||
|
||||
|
||||
def get_server_hardware_id(node):
|
||||
"""Validates node configuration and returns xclarity hardware id.
|
||||
|
||||
Validates whether node configuration is consistent with XClarity and
|
||||
returns the XClarity Hardware ID for a specific node.
|
||||
:param node: node object to get information from
|
||||
:returns: the XClarity Hardware ID for a specific node
|
||||
:raises: MissingParameterValue if unable to validate XClarity Hardware ID
|
||||
|
||||
"""
|
||||
xclarity_hardware_id = node.driver_info.get('xclarity_hardware_id')
|
||||
if not xclarity_hardware_id:
|
||||
msg = (_("Error validating node driver info, "
|
||||
"server uuid: %s missing xclarity_hardware_id") %
|
||||
node.uuid)
|
||||
raise exception.MissingParameterValue(err=msg)
|
||||
return xclarity_hardware_id
|
||||
|
||||
|
||||
def translate_xclarity_power_state(power_state):
|
||||
"""Translates XClarity's power state strings to be consistent with Ironic.
|
||||
|
||||
:param power_state: power state string to be translated
|
||||
:returns: the translated power state
|
||||
"""
|
||||
power_states_map = {
|
||||
xclarity_client_constants.STATE_POWER_ON: states.POWER_ON,
|
||||
xclarity_client_constants.STATE_POWER_OFF: states.POWER_OFF,
|
||||
}
|
||||
|
||||
return power_states_map.get(power_state, states.ERROR)
|
||||
|
||||
|
||||
def translate_xclarity_power_action(power_action):
|
||||
"""Translates ironic's power action strings to XClarity's format.
|
||||
|
||||
:param power_action: power action string to be translated
|
||||
:returns: the power action translated
|
||||
"""
|
||||
|
||||
power_action_map = {
|
||||
states.POWER_ON: xclarity_client_constants.ACTION_POWER_ON,
|
||||
states.POWER_OFF: xclarity_client_constants.ACTION_POWER_OFF,
|
||||
states.REBOOT: xclarity_client_constants.ACTION_REBOOT
|
||||
}
|
||||
|
||||
return power_action_map[power_action]
|
@ -1,248 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
#
|
||||
# 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_lib import metrics_utils
|
||||
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 _
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules.xclarity import common
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
xclarity_client_exceptions = importutils.try_import(
|
||||
'xclarity_client.exceptions')
|
||||
|
||||
BOOT_DEVICE_MAPPING_TO_XCLARITY = {
|
||||
boot_devices.PXE: 'PXE Network',
|
||||
boot_devices.DISK: 'Hard Disk 0',
|
||||
boot_devices.CDROM: 'CD/DVD Rom',
|
||||
boot_devices.BIOS: 'Boot To F1'
|
||||
}
|
||||
|
||||
BOOT_DEVICE_MAPPING_FROM_XCLARITY = {
|
||||
v: k for k, v in BOOT_DEVICE_MAPPING_TO_XCLARITY.items()}
|
||||
|
||||
SUPPORTED_BOOT_DEVICES = [
|
||||
boot_devices.PXE,
|
||||
boot_devices.DISK,
|
||||
boot_devices.CDROM,
|
||||
boot_devices.BIOS,
|
||||
]
|
||||
|
||||
|
||||
class XClarityManagement(base.ManagementInterface):
|
||||
|
||||
# NOTE(TheJulia): Deprecating November 2023 in favor of Redfish
|
||||
# and due to a lack of active driver maintenance.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return common.COMMON_PROPERTIES
|
||||
|
||||
@METRICS.timer('XClarityManagement.validate')
|
||||
def validate(self, task):
|
||||
"""Validate the driver-specific info supplied.
|
||||
|
||||
This method validates if the 'driver_info' property of the supplied
|
||||
task's node contains the required information for this driver to
|
||||
manage the node.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
"""
|
||||
common.parse_driver_info(task.node)
|
||||
|
||||
@METRICS.timer('XClarityManagement.get_supported_boot_devices')
|
||||
def get_supported_boot_devices(self, task):
|
||||
"""Gets 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 SUPPORTED_BOOT_DEVICES
|
||||
|
||||
def _validate_supported_boot_device(self, task, boot_device):
|
||||
"""It validates if the boot device is supported by XClarity.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param boot_device: the boot device in XClarity format, one of
|
||||
['PXE Network', 'Hard Disk 0', 'CD/DVD Rom', 'Boot To F1']
|
||||
:raises: InvalidParameterValue if the boot device is not supported.
|
||||
"""
|
||||
if boot_device not in BOOT_DEVICE_MAPPING_FROM_XCLARITY:
|
||||
raise exception.InvalidParameterValue(
|
||||
_("Unsupported boot device %(device)s for node: %(node)s ")
|
||||
% {"device": boot_device, "node": task.node.uuid}
|
||||
)
|
||||
|
||||
@METRICS.timer('XClarityManagement.get_boot_device')
|
||||
def get_boot_device(self, task):
|
||||
"""Get the current boot device for the task's node.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: a dictionary containing:
|
||||
:boot_device: the boot device, one of [PXE, DISK, CDROM, BIOS]
|
||||
:persistent: Whether the boot device will persist or not
|
||||
It returns None if boot device is unknown.
|
||||
:raises: InvalidParameterValue if the boot device is unknown
|
||||
:raises: XClarityError if the communication with XClarity fails
|
||||
"""
|
||||
node = task.node
|
||||
client = common.get_xclarity_client(node)
|
||||
server_hardware_id = common.get_server_hardware_id(node)
|
||||
try:
|
||||
boot_info = (
|
||||
client.get_node_all_boot_info(
|
||||
server_hardware_id)
|
||||
)
|
||||
except xclarity_client_exceptions.XClarityError as xclarity_exc:
|
||||
LOG.error(
|
||||
"Error getting boot device from XClarity for node %(node)s. "
|
||||
"Error: %(error)s", {'node': node.uuid,
|
||||
'error': xclarity_exc})
|
||||
raise exception.XClarityError(error=xclarity_exc)
|
||||
|
||||
persistent = False
|
||||
primary = None
|
||||
boot_order = boot_info['bootOrder']['bootOrderList']
|
||||
for item in boot_order:
|
||||
current = item.get('currentBootOrderDevices')
|
||||
if current is None:
|
||||
LOG.warning(
|
||||
'Current boot order is None from XClarity for '
|
||||
'node %(node)s. Please check the hardware and '
|
||||
'XClarity connection', {'node': node.uuid, })
|
||||
return {'boot_device': None, 'persistent': None}
|
||||
else:
|
||||
primary = current[0]
|
||||
boot_type = item.get('bootType')
|
||||
if boot_type == "SingleUse":
|
||||
persistent = False
|
||||
if primary != 'None':
|
||||
self._validate_supported_boot_device(task, primary)
|
||||
boot_device = {
|
||||
'boot_device':
|
||||
BOOT_DEVICE_MAPPING_FROM_XCLARITY.get(primary),
|
||||
'persistent': persistent
|
||||
}
|
||||
return boot_device
|
||||
elif boot_type == "Permanent":
|
||||
persistent = True
|
||||
if primary != 'None':
|
||||
self._validate_supported_boot_device(task, primary)
|
||||
boot_device = {
|
||||
'boot_device':
|
||||
BOOT_DEVICE_MAPPING_FROM_XCLARITY.get(primary),
|
||||
'persistent': persistent
|
||||
}
|
||||
return boot_device
|
||||
else:
|
||||
return {'boot_device': None, 'persistent': None}
|
||||
|
||||
@METRICS.timer('XClarityManagement.set_boot_device')
|
||||
@task_manager.require_exclusive_lock
|
||||
def set_boot_device(self, task, device, persistent=False):
|
||||
"""Sets the boot device for a node.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param device: the boot device, one of the supported devices
|
||||
listed in :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.
|
||||
:raises: InvalidParameterValue if an invalid boot device is
|
||||
specified.
|
||||
:raises: XClarityError if the communication with XClarity fails
|
||||
"""
|
||||
node = task.node
|
||||
xc_device = self._translate_ironic_to_xclarity(device)
|
||||
|
||||
server_hardware_id = common.get_server_hardware_id(node)
|
||||
LOG.debug("Setting boot device to %(device)s for node %(node)s",
|
||||
{"device": device, "node": node.uuid})
|
||||
self._set_boot_device(task, server_hardware_id, xc_device,
|
||||
singleuse=not persistent)
|
||||
|
||||
@METRICS.timer('XClarityManagement.get_sensors_data')
|
||||
def get_sensors_data(self, task):
|
||||
"""Get sensors data.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:raises: NotImplementedError
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _translate_ironic_to_xclarity(self, boot_device):
|
||||
"""Translates Ironic boot options to Xclarity boot options.
|
||||
|
||||
:param boot_device: Ironic boot_device
|
||||
:returns: Translated XClarity boot_device.
|
||||
|
||||
"""
|
||||
return BOOT_DEVICE_MAPPING_TO_XCLARITY.get(boot_device)
|
||||
|
||||
def _set_boot_device(self, task, server_hardware_id,
|
||||
new_primary_boot_device, singleuse=False):
|
||||
"""Set the current boot device for xclarity
|
||||
|
||||
:param server_hardware_id: the uri of the server hardware in XClarity
|
||||
:param new_primary_boot_device: boot device to be set
|
||||
:param task: a TaskManager instance.
|
||||
:param singleuse: if this device will be used only once at next boot
|
||||
"""
|
||||
node = task.node
|
||||
client = common.get_xclarity_client(node)
|
||||
boot_info = client.get_node_all_boot_info(
|
||||
server_hardware_id)
|
||||
current = []
|
||||
LOG.debug(
|
||||
("Setting boot device to %(device)s for XClarity "
|
||||
"node %(node)s"),
|
||||
{'device': new_primary_boot_device, 'node': node.uuid}
|
||||
)
|
||||
for item in boot_info['bootOrder']['bootOrderList']:
|
||||
if singleuse and item['bootType'] == 'SingleUse':
|
||||
item['currentBootOrderDevices'][0] = new_primary_boot_device
|
||||
elif not singleuse and item['bootType'] == 'Permanent':
|
||||
current = item['currentBootOrderDevices']
|
||||
if new_primary_boot_device == current[0]:
|
||||
return
|
||||
if new_primary_boot_device in current:
|
||||
current.remove(new_primary_boot_device)
|
||||
current.insert(0, new_primary_boot_device)
|
||||
item['currentBootOrderDevices'] = current
|
||||
|
||||
try:
|
||||
client.set_node_boot_info(server_hardware_id,
|
||||
boot_info,
|
||||
new_primary_boot_device,
|
||||
singleuse)
|
||||
except xclarity_client_exceptions.XClarityError as xclarity_exc:
|
||||
LOG.error(
|
||||
('Error setting boot device %(boot_device)s for the XClarity '
|
||||
'node %(node)s. Error: %(error)s'),
|
||||
{'boot_device': new_primary_boot_device, 'node': node.uuid,
|
||||
'error': xclarity_exc}
|
||||
)
|
||||
raise exception.XClarityError(error=xclarity_exc)
|
@ -1,134 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
#
|
||||
# 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_lib import metrics_utils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules.xclarity import common
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
METRICS = metrics_utils.get_metrics_logger(__name__)
|
||||
|
||||
xclarity_client_exceptions = importutils.try_import(
|
||||
'xclarity_client.exceptions')
|
||||
|
||||
|
||||
class XClarityPower(base.PowerInterface):
|
||||
|
||||
# NOTE(TheJulia): Deprecating November 2023 in favor of Redfish
|
||||
# and due to a lack of active driver maintenance.
|
||||
supported = False
|
||||
|
||||
def get_properties(self):
|
||||
return common.get_properties()
|
||||
|
||||
@METRICS.timer('XClarityPower.validate')
|
||||
def validate(self, task):
|
||||
"""Validate the driver-specific info supplied.
|
||||
|
||||
This method validates if the 'driver_info' property of the supplied
|
||||
task's node contains the required information for this driver to
|
||||
manage the power state of the node.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
"""
|
||||
common.parse_driver_info(task.node)
|
||||
|
||||
@METRICS.timer('XClarityPower.get_power_state')
|
||||
def get_power_state(self, task):
|
||||
"""Gets the current power state.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:returns: one of :mod:`ironic.common.states` POWER_OFF,
|
||||
POWER_ON or ERROR.
|
||||
:raises: XClarityError if fails to retrieve power state of XClarity
|
||||
resource
|
||||
"""
|
||||
node = task.node
|
||||
client = common.get_xclarity_client(node)
|
||||
server_hardware_id = common.get_server_hardware_id(node)
|
||||
try:
|
||||
power_state = client.get_node_power_status(server_hardware_id)
|
||||
except xclarity_client_exceptions.XClarityError as xclarity_exc:
|
||||
LOG.error(
|
||||
("Error getting power state for node %(node)s. Error: "
|
||||
"%(error)s"),
|
||||
{'node': node.uuid, 'error': xclarity_exc}
|
||||
)
|
||||
raise exception.XClarityError(error=xclarity_exc)
|
||||
return common.translate_xclarity_power_state(power_state)
|
||||
|
||||
@METRICS.timer('XClarityPower.set_power_state')
|
||||
@task_manager.require_exclusive_lock
|
||||
def set_power_state(self, task, power_state, timeout=None):
|
||||
"""Turn the current power state on or off.
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:param power_state: The desired power state POWER_ON, POWER_OFF or
|
||||
REBOOT from :mod:`ironic.common.states`.
|
||||
:param timeout: timeout (in seconds). Unsupported by this interface.
|
||||
:raises: InvalidParameterValue if an invalid power state was specified.
|
||||
:raises: XClarityError if XClarity fails setting the power state.
|
||||
"""
|
||||
# TODO(rloo): Support timeouts!
|
||||
if timeout is not None:
|
||||
LOG.warning(
|
||||
"The 'xclarity' Power Interface's 'set_power_state' method "
|
||||
"doesn't support the 'timeout' parameter. Ignoring "
|
||||
"timeout=%(timeout)s",
|
||||
{'timeout': timeout})
|
||||
|
||||
if power_state == states.REBOOT:
|
||||
target_power_state = self.get_power_state(task)
|
||||
if target_power_state == states.POWER_OFF:
|
||||
power_state = states.POWER_ON
|
||||
|
||||
node = task.node
|
||||
client = common.get_xclarity_client(node)
|
||||
server_hardware_id = common.get_server_hardware_id(node)
|
||||
LOG.debug("Setting power state of node %(node_uuid)s to "
|
||||
"%(power_state)s",
|
||||
{'node_uuid': node.uuid, 'power_state': power_state})
|
||||
|
||||
try:
|
||||
client.set_node_power_status(server_hardware_id, power_state)
|
||||
except xclarity_client_exceptions.XClarityError as xclarity_exc:
|
||||
LOG.error(
|
||||
"Error setting power state of node %(node_uuid)s to "
|
||||
"%(power_state)s",
|
||||
{'node_uuid': task.node.uuid, 'power_state': power_state})
|
||||
raise exception.XClarityError(error=xclarity_exc)
|
||||
|
||||
@METRICS.timer('XClarityPower.reboot')
|
||||
@task_manager.require_exclusive_lock
|
||||
def reboot(self, task, timeout=None):
|
||||
"""Soft reboot the node
|
||||
|
||||
:param task: a TaskManager instance.
|
||||
:param timeout: timeout (in seconds). Unsupported by this interface.
|
||||
"""
|
||||
# TODO(rloo): Support timeouts!
|
||||
if timeout is not None:
|
||||
LOG.warning("The 'xclarity' Power Interface's 'reboot' method "
|
||||
"doesn't support the 'timeout' parameter. Ignoring "
|
||||
"timeout=%(timeout)s",
|
||||
{'timeout': timeout})
|
||||
|
||||
self.set_power_state(task, states.REBOOT)
|
@ -1,35 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
XClarity Driver and supporting meta-classes.
|
||||
"""
|
||||
|
||||
from ironic.drivers import generic
|
||||
from ironic.drivers.modules.xclarity import management
|
||||
from ironic.drivers.modules.xclarity import power
|
||||
|
||||
|
||||
class XClarityHardware(generic.GenericHardware):
|
||||
"""XClarity hardware type. """
|
||||
|
||||
@property
|
||||
def supported_management_interfaces(self):
|
||||
"""List of supported management interfaces."""
|
||||
return [management.XClarityManagement]
|
||||
|
||||
@property
|
||||
def supported_power_interfaces(self):
|
||||
"""List of supported power interfaces."""
|
||||
return [power.XClarityPower]
|
@ -514,24 +514,6 @@ def create_test_node_tag(**kw):
|
||||
return dbapi.add_node_tag(tag['node_id'], tag['tag'])
|
||||
|
||||
|
||||
def get_test_xclarity_properties():
|
||||
return {
|
||||
"cpu_arch": "x86_64",
|
||||
"local_gb": "10",
|
||||
"memory_mb": "4096",
|
||||
}
|
||||
|
||||
|
||||
def get_test_xclarity_driver_info():
|
||||
return {
|
||||
'xclarity_manager_ip': "1.2.3.4",
|
||||
'xclarity_username': "USERID",
|
||||
'xclarity_password': "fake",
|
||||
'xclarity_port': 443,
|
||||
'xclarity_hardware_id': 'fake_sh_id',
|
||||
}
|
||||
|
||||
|
||||
def get_test_node_trait(**kw):
|
||||
return {
|
||||
'version': kw.get('version', trait.Trait.VERSION),
|
||||
|
@ -1,113 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.drivers.modules.xclarity import common
|
||||
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
|
||||
|
||||
xclarity_client = importutils.try_import('xclarity_client.client')
|
||||
xclarity_exceptions = importutils.try_import('xclarity_client.exceptions')
|
||||
xclarity_constants = importutils.try_import('xclarity_client.constants')
|
||||
|
||||
INFO_DICT = db_utils.get_test_xclarity_driver_info()
|
||||
|
||||
|
||||
class XClarityCommonTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(XClarityCommonTestCase, self).setUp()
|
||||
self.config(enabled_hardware_types=['xclarity'],
|
||||
enabled_power_interfaces=['xclarity'],
|
||||
enabled_management_interfaces=['xclarity'])
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context, driver='xclarity',
|
||||
properties=db_utils.get_test_xclarity_properties(),
|
||||
driver_info=INFO_DICT)
|
||||
|
||||
def test_parse_driver_info(self):
|
||||
info = common.parse_driver_info(self.node)
|
||||
self.assertEqual(INFO_DICT['xclarity_manager_ip'],
|
||||
info['xclarity_manager_ip'])
|
||||
self.assertEqual(INFO_DICT['xclarity_username'],
|
||||
info['xclarity_username'])
|
||||
self.assertEqual(INFO_DICT['xclarity_password'],
|
||||
info['xclarity_password'])
|
||||
self.assertEqual(INFO_DICT['xclarity_port'], info['xclarity_port'])
|
||||
self.assertEqual(INFO_DICT['xclarity_hardware_id'],
|
||||
info['xclarity_hardware_id'])
|
||||
|
||||
def test_parse_driver_info_missing_hardware_id(self):
|
||||
del self.node.driver_info['xclarity_hardware_id']
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
|
||||
def test_parse_driver_info_get_param_from_config(self):
|
||||
del self.node.driver_info['xclarity_manager_ip']
|
||||
del self.node.driver_info['xclarity_username']
|
||||
del self.node.driver_info['xclarity_password']
|
||||
self.config(manager_ip='5.6.7.8', group='xclarity')
|
||||
self.config(username='user', group='xclarity')
|
||||
self.config(password='password', group='xclarity')
|
||||
info = common.parse_driver_info(self.node)
|
||||
self.assertEqual('5.6.7.8', info['xclarity_manager_ip'])
|
||||
self.assertEqual('user', info['xclarity_username'])
|
||||
self.assertEqual('password', info['xclarity_password'])
|
||||
|
||||
def test_parse_driver_info_missing_driver_info_and_config(self):
|
||||
del self.node.driver_info['xclarity_manager_ip']
|
||||
del self.node.driver_info['xclarity_username']
|
||||
del self.node.driver_info['xclarity_password']
|
||||
e = self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
self.assertIn('xclarity_manager_ip', str(e))
|
||||
self.assertIn('xclarity_username', str(e))
|
||||
self.assertIn('xclarity_password', str(e))
|
||||
|
||||
def test_parse_driver_info_invalid_port(self):
|
||||
self.node.driver_info['xclarity_port'] = 'asd'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
self.node.driver_info['xclarity_port'] = '65536'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
self.node.driver_info['xclarity_port'] = 'invalid'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
self.node.driver_info['xclarity_port'] = '-1'
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
common.parse_driver_info, self.node)
|
||||
|
||||
def test_get_xclarity_client(self):
|
||||
if not mock._is_instance_mock(xclarity_client):
|
||||
mock.patch.object(xclarity_client, 'Client', autospec=True).start()
|
||||
mock_xclarityclient = xclarity_client.Client
|
||||
expected_call = mock.call(ip='1.2.3.4', password='fake', port=443,
|
||||
username='USERID')
|
||||
common.get_xclarity_client(self.node)
|
||||
|
||||
self.assertEqual(mock_xclarityclient.mock_calls, [expected_call])
|
||||
|
||||
def test_get_server_hardware_id(self):
|
||||
driver_info = self.node.driver_info
|
||||
driver_info['xclarity_hardware_id'] = 'test'
|
||||
self.node.driver_info = driver_info
|
||||
result = common.get_server_hardware_id(self.node)
|
||||
self.assertEqual(result, 'test')
|
@ -1,158 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.xclarity import common
|
||||
from ironic.drivers.modules.xclarity import management
|
||||
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
|
||||
|
||||
|
||||
xclarity_client_exceptions = importutils.try_import(
|
||||
'xclarity_client.exceptions')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_xclarity_client', spect_set=True,
|
||||
autospec=True)
|
||||
class XClarityManagementDriverTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(XClarityManagementDriverTestCase, self).setUp()
|
||||
self.config(enabled_hardware_types=['xclarity'],
|
||||
enabled_power_interfaces=['xclarity'],
|
||||
enabled_management_interfaces=['xclarity'])
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver='xclarity',
|
||||
driver_info=db_utils.get_test_xclarity_driver_info())
|
||||
|
||||
@mock.patch.object(common, 'get_server_hardware_id',
|
||||
spect_set=True, autospec=True)
|
||||
def test_validate(self, mock_validate, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.validate(task)
|
||||
common.get_server_hardware_id(task.node)
|
||||
mock_validate.assert_called_with(task.node)
|
||||
|
||||
def test_get_properties(self, mock_get_xc_client):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
driver = management.XClarityManagement()
|
||||
self.assertEqual(expected, driver.get_properties())
|
||||
|
||||
@mock.patch.object(management.XClarityManagement, 'get_boot_device',
|
||||
return_value='pxe', autospec=True)
|
||||
def test_set_boot_device(self, mock_get_boot_device,
|
||||
mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.set_boot_device(task, 'pxe')
|
||||
result = task.driver.management.get_boot_device(task)
|
||||
self.assertEqual(result, 'pxe')
|
||||
|
||||
def test_set_boot_device_fail(self, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
xclarity_client_exceptions.XClarityError = Exception
|
||||
sys.modules['xclarity_client.exceptions'] = (
|
||||
xclarity_client_exceptions)
|
||||
if 'ironic.drivers.modules.xclarity' in sys.modules:
|
||||
importlib.reload(
|
||||
sys.modules['ironic.drivers.modules.xclarity'])
|
||||
ex = exception.XClarityError('E')
|
||||
mock_get_xc_client.return_value.set_node_boot_info.side_effect = ex
|
||||
self.assertRaises(exception.XClarityError,
|
||||
task.driver.management.set_boot_device,
|
||||
task,
|
||||
"pxe")
|
||||
|
||||
def test_get_supported_boot_devices(self, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
expected = [boot_devices.PXE, boot_devices.BIOS,
|
||||
boot_devices.DISK, boot_devices.CDROM]
|
||||
self.assertCountEqual(
|
||||
expected,
|
||||
task.driver.management.get_supported_boot_devices(task))
|
||||
|
||||
@mock.patch.object(
|
||||
management.XClarityManagement,
|
||||
'get_boot_device',
|
||||
return_value={'boot_device': 'pxe', 'persistent': False},
|
||||
autospec=True)
|
||||
def test_get_boot_device(self, mock_get_boot_device, mock_get_xc_client):
|
||||
reference = {'boot_device': 'pxe', 'persistent': False}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
expected_boot_device = task.driver.management.get_boot_device(
|
||||
task=task)
|
||||
|
||||
self.assertEqual(reference, expected_boot_device)
|
||||
|
||||
def test_get_boot_device_fail(self, mock_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
xclarity_client_exceptions.XClarityError = Exception
|
||||
sys.modules['xclarity_client.exceptions'] = (
|
||||
xclarity_client_exceptions)
|
||||
if 'ironic.drivers.modules.xclarity' in sys.modules:
|
||||
importlib.reload(
|
||||
sys.modules['ironic.drivers.modules.xclarity'])
|
||||
ex = exception.XClarityError('E')
|
||||
mock_xc_client.return_value.get_node_all_boot_info.side_effect = ex
|
||||
self.assertRaises(
|
||||
exception.XClarityError,
|
||||
task.driver.management.get_boot_device,
|
||||
task)
|
||||
|
||||
def test_get_boot_device_current_none(self, mock_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
reference = {'boot_device': None, 'persistent': None}
|
||||
mock_xc_client.return_value.get_node_all_boot_info.return_value = \
|
||||
{
|
||||
'bootOrder': {
|
||||
'bootOrderList': [{
|
||||
'fakeBootOrderDevices': []
|
||||
}]
|
||||
}
|
||||
}
|
||||
expected_boot_device = task.driver.management.get_boot_device(
|
||||
task=task)
|
||||
self.assertEqual(reference, expected_boot_device)
|
||||
|
||||
def test_get_boot_device_primary_none(self, mock_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
reference = {'boot_device': None, 'persistent': None}
|
||||
mock_xc_client.return_value.get_node_all_boot_info.return_value = \
|
||||
{
|
||||
'bootOrder': {
|
||||
'bootOrderList': [
|
||||
{
|
||||
'bootType': 'SingleUse',
|
||||
'CurrentBootOrderDevices': []
|
||||
},
|
||||
{
|
||||
'bootType': 'Permanent',
|
||||
'CurrentBootOrderDevices': []
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
expected_boot_device = task.driver.management.get_boot_device(
|
||||
task=task)
|
||||
self.assertEqual(reference, expected_boot_device)
|
@ -1,146 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.xclarity import common
|
||||
from ironic.drivers.modules.xclarity import power
|
||||
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
|
||||
|
||||
STATE_POWER_ON = "power on"
|
||||
STATE_POWER_OFF = "power off"
|
||||
STATE_POWERING_ON = "power on"
|
||||
STATE_POWERING_OFF = "power on"
|
||||
|
||||
xclarity_constants = importutils.try_import('xclarity_client.constants')
|
||||
xclarity_client_exceptions = importutils.try_import(
|
||||
'xclarity_client.exceptions')
|
||||
|
||||
|
||||
@mock.patch.object(common, 'get_xclarity_client',
|
||||
spect_set=True, autospec=True)
|
||||
class XClarityPowerDriverTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(XClarityPowerDriverTestCase, self).setUp()
|
||||
self.config(enabled_hardware_types=['xclarity'],
|
||||
enabled_power_interfaces=['xclarity'],
|
||||
enabled_management_interfaces=['xclarity'])
|
||||
self.node = obj_utils.create_test_node(
|
||||
self.context,
|
||||
driver='xclarity',
|
||||
driver_info=db_utils.get_test_xclarity_driver_info())
|
||||
|
||||
def test_get_properties(self, mock_get_xc_client):
|
||||
expected = common.COMMON_PROPERTIES
|
||||
driver = power.XClarityPower()
|
||||
self.assertEqual(expected, driver.get_properties())
|
||||
|
||||
@mock.patch.object(common, 'get_server_hardware_id',
|
||||
spect_set=True, autospec=True)
|
||||
def test_validate(self, mock_validate_driver_info, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.validate(task)
|
||||
common.get_server_hardware_id(task.node)
|
||||
mock_validate_driver_info.assert_called_with(task.node)
|
||||
|
||||
@mock.patch.object(power.XClarityPower, 'get_power_state',
|
||||
return_value=STATE_POWER_ON, autospec=True)
|
||||
def test_get_power_state(self, mock_get_power_state, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
result = power.XClarityPower.get_power_state(self, task)
|
||||
self.assertEqual(STATE_POWER_ON, result)
|
||||
|
||||
@mock.patch.object(common, 'translate_xclarity_power_state',
|
||||
spec_set=True, autospec=True)
|
||||
def test_get_power_state_fail(self, mock_translate_state, mock_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
xclarity_client_exceptions.XClarityError = Exception
|
||||
sys.modules['xclarity_client.exceptions'] = (
|
||||
xclarity_client_exceptions)
|
||||
if 'ironic.drivers.modules.xclarity' in sys.modules:
|
||||
importlib.reload(
|
||||
sys.modules['ironic.drivers.modules.xclarity'])
|
||||
ex = exception.XClarityError('E')
|
||||
mock_xc_client.return_value.get_node_power_status.side_effect = ex
|
||||
self.assertRaises(exception.XClarityError,
|
||||
task.driver.power.get_power_state,
|
||||
task)
|
||||
self.assertFalse(mock_translate_state.called)
|
||||
|
||||
@mock.patch.object(power.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(power.XClarityPower, 'get_power_state',
|
||||
return_value=states.POWER_ON, autospec=True)
|
||||
def test_set_power(self, mock_set_power_state, mock_log,
|
||||
mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.set_power_state(task, states.POWER_ON)
|
||||
expected = task.driver.power.get_power_state(task)
|
||||
self.assertEqual(expected, states.POWER_ON)
|
||||
self.assertFalse(mock_log.called)
|
||||
|
||||
@mock.patch.object(power.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(power.XClarityPower, 'get_power_state',
|
||||
return_value=states.POWER_ON, autospec=True)
|
||||
def test_set_power_timeout(self, mock_set_power_state, mock_log,
|
||||
mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.set_power_state(task, states.POWER_ON,
|
||||
timeout=21)
|
||||
expected = task.driver.power.get_power_state(task)
|
||||
self.assertEqual(expected, states.POWER_ON)
|
||||
self.assertTrue(mock_log.called)
|
||||
|
||||
def test_set_power_fail(self, mock_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
xclarity_client_exceptions.XClarityError = Exception
|
||||
sys.modules['xclarity_client.exceptions'] = (
|
||||
xclarity_client_exceptions)
|
||||
if 'ironic.drivers.modules.xclarity' in sys.modules:
|
||||
importlib.reload(
|
||||
sys.modules['ironic.drivers.modules.xclarity'])
|
||||
ex = exception.XClarityError('E')
|
||||
mock_xc_client.return_value.set_node_power_status.side_effect = ex
|
||||
self.assertRaises(exception.XClarityError,
|
||||
task.driver.power.set_power_state,
|
||||
task, states.POWER_OFF)
|
||||
|
||||
@mock.patch.object(power.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(power.XClarityPower, 'set_power_state', autospec=True)
|
||||
def test_reboot(self, mock_set_power_state, mock_log, mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.reboot(task)
|
||||
mock_set_power_state.assert_called_with(
|
||||
mock.ANY, task, states.REBOOT)
|
||||
self.assertFalse(mock_log.called)
|
||||
|
||||
@mock.patch.object(power.LOG, 'warning', autospec=True)
|
||||
@mock.patch.object(power.XClarityPower, 'set_power_state', autospec=True)
|
||||
def test_reboot_timeout(self, mock_set_power_state, mock_log,
|
||||
mock_get_xc_client):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.power.reboot(task, timeout=55)
|
||||
mock_set_power_state.assert_called_with(
|
||||
mock.ANY, task, states.REBOOT)
|
||||
self.assertTrue(mock_log.called)
|
@ -1,46 +0,0 @@
|
||||
# Copyright 2017 Lenovo, Inc.
|
||||
#
|
||||
# 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 XClarity Driver
|
||||
"""
|
||||
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import pxe
|
||||
from ironic.drivers.xclarity import management as xc_management
|
||||
from ironic.drivers.xclarity import power as xc_power
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
|
||||
class XClarityHardwareTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(XClarityHardwareTestCase, self).setUp()
|
||||
self.config(enabled_hardware_types=['xclarity'],
|
||||
enabled_power_interfaces=['xclarity'],
|
||||
enabled_management_interfaces=['xclarity'])
|
||||
|
||||
def test_default_interfaces(self):
|
||||
node = obj_utils.create_test_node(self.context, driver='xclarity')
|
||||
with task_manager.acquire(self.context, node.id) as task:
|
||||
self.assertIsInstance(task.driver.boot,
|
||||
pxe.PXEBoot)
|
||||
self.assertIsInstance(task.driver.deploy,
|
||||
agent.AgentDeploy)
|
||||
self.assertIsInstance(task.driver.management,
|
||||
xc_management.XClarityManagement)
|
||||
self.assertIsInstance(task.driver.power,
|
||||
xc_power.XClarityPower)
|
@ -122,24 +122,6 @@ REDFISH_SPEC = (
|
||||
'redfish',
|
||||
)
|
||||
|
||||
XCLARITY_SPEC = (
|
||||
'client',
|
||||
'states',
|
||||
'exceptions',
|
||||
'models',
|
||||
'utils',
|
||||
)
|
||||
|
||||
XCLARITY_CLIENT_CLS_SPEC = (
|
||||
)
|
||||
|
||||
XCLARITY_STATES_SPEC = (
|
||||
'STATE_POWERING_OFF',
|
||||
'STATE_POWERING_ON',
|
||||
'STATE_POWER_OFF',
|
||||
'STATE_POWER_ON',
|
||||
)
|
||||
|
||||
# python-ibmcclient
|
||||
IBMCCLIENT_SPEC = (
|
||||
'connect',
|
||||
|
@ -196,25 +196,6 @@ class MockKwargsException(Exception):
|
||||
self.kwargs = kwargs
|
||||
|
||||
|
||||
xclarity_client = importutils.try_import('xclarity_client')
|
||||
if not xclarity_client:
|
||||
xclarity_client = mock.MagicMock(spec_set=mock_specs.XCLARITY_SPEC)
|
||||
sys.modules['xclarity_client'] = xclarity_client
|
||||
sys.modules['xclarity_client.client'] = xclarity_client.client
|
||||
states = mock.MagicMock(
|
||||
spec_set=mock_specs.XCLARITY_STATES_SPEC,
|
||||
STATE_POWER_ON="power on",
|
||||
STATE_POWER_OFF="power off",
|
||||
STATE_POWERING_ON="powering_on",
|
||||
STATE_POWERING_OFF="powering_off")
|
||||
sys.modules['xclarity_client.states'] = states
|
||||
sys.modules['xclarity_client.exceptions'] = xclarity_client.exceptions
|
||||
sys.modules['xclarity_client.utils'] = xclarity_client.utils
|
||||
xclarity_client.exceptions.XClarityException = type('XClarityException',
|
||||
(Exception,), {})
|
||||
sys.modules['xclarity_client.models'] = xclarity_client.models
|
||||
|
||||
|
||||
# python-ibmcclient mocks for HUAWEI rack server driver
|
||||
ibmc_client = importutils.try_import('ibmc_client')
|
||||
if not ibmc_client:
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The deprecated ``xclarity`` hardware type has been removed from Ironic.
|
@ -130,7 +130,6 @@ ironic.hardware.interfaces.management =
|
||||
irmc = ironic.drivers.modules.irmc.management:IRMCManagement
|
||||
noop = ironic.drivers.modules.noop_mgmt:NoopManagement
|
||||
redfish = ironic.drivers.modules.redfish.management:RedfishManagement
|
||||
xclarity = ironic.drivers.modules.xclarity.management:XClarityManagement
|
||||
|
||||
ironic.hardware.interfaces.network =
|
||||
flat = ironic.drivers.modules.network.flat:FlatNetwork
|
||||
@ -149,7 +148,6 @@ ironic.hardware.interfaces.power =
|
||||
irmc = ironic.drivers.modules.irmc.power:IRMCPower
|
||||
redfish = ironic.drivers.modules.redfish.power:RedfishPower
|
||||
snmp = ironic.drivers.modules.snmp:SNMPPower
|
||||
xclarity = ironic.drivers.modules.xclarity.power:XClarityPower
|
||||
|
||||
ironic.hardware.interfaces.raid =
|
||||
agent = ironic.drivers.modules.agent:AgentRAID
|
||||
@ -198,7 +196,6 @@ ironic.hardware.types =
|
||||
manual-management = ironic.drivers.generic:ManualManagementHardware
|
||||
redfish = ironic.drivers.redfish:RedfishHardware
|
||||
snmp = ironic.drivers.snmp:SNMPHardware
|
||||
xclarity = ironic.drivers.xclarity:XClarityHardware
|
||||
|
||||
ironic.database.migration_backend =
|
||||
sqlalchemy = ironic.db.sqlalchemy.migration
|
||||
|
Loading…
Reference in New Issue
Block a user