Merge "Remove deprecated xclarity hardware type"

This commit is contained in:
Zuul 2024-06-19 13:53:09 +00:00 committed by Gerrit Code Review
commit 8a209f1372
21 changed files with 4 additions and 1260 deletions

View File

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

View File

@ -25,7 +25,6 @@ Hardware Types
drivers/irmc
drivers/redfish
drivers/snmp
drivers/xclarity
drivers/fake
Changing Hardware Types and Interfaces

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,4 @@
---
upgrade:
- |
The deprecated ``xclarity`` hardware type has been removed from Ironic.

View File

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