Merge "Add XClarity Driver"
This commit is contained in:
commit
83e3afbdee
@ -13,6 +13,7 @@ python-ilorest-library>=2.1.0
|
|||||||
hpOneView>=4.4.0
|
hpOneView>=4.4.0
|
||||||
UcsSdk==0.8.2.2
|
UcsSdk==0.8.2.2
|
||||||
python-dracclient>=1.3.0
|
python-dracclient>=1.3.0
|
||||||
|
python-xclarityclient>=0.1.6
|
||||||
|
|
||||||
# The CIMC drivers use the Cisco IMC SDK version 0.7.2 or greater
|
# The CIMC drivers use the Cisco IMC SDK version 0.7.2 or greater
|
||||||
ImcSdk>=0.7.2
|
ImcSdk>=0.7.2
|
||||||
|
@ -4233,3 +4233,24 @@
|
|||||||
# for endpoint URL discovery. Mutually exclusive with
|
# for endpoint URL discovery. Mutually exclusive with
|
||||||
# min_version and max_version (string value)
|
# min_version and max_version (string value)
|
||||||
#version = <None>
|
#version = <None>
|
||||||
|
|
||||||
|
|
||||||
|
[xclarity]
|
||||||
|
|
||||||
|
#
|
||||||
|
# From ironic
|
||||||
|
#
|
||||||
|
|
||||||
|
# IP address of XClarity controller. (string value)
|
||||||
|
#manager_ip = <None>
|
||||||
|
|
||||||
|
# Username to access the XClarity controller. (string value)
|
||||||
|
#username = <None>
|
||||||
|
|
||||||
|
# Password for XClarity controller username. (string value)
|
||||||
|
#password = <None>
|
||||||
|
|
||||||
|
# Port to be used for XClarity operations. (port value)
|
||||||
|
# Minimum value: 0
|
||||||
|
# Maximum value: 65535
|
||||||
|
#port = 443
|
||||||
|
@ -44,6 +44,7 @@ from ironic.conf import redfish
|
|||||||
from ironic.conf import service_catalog
|
from ironic.conf import service_catalog
|
||||||
from ironic.conf import snmp
|
from ironic.conf import snmp
|
||||||
from ironic.conf import swift
|
from ironic.conf import swift
|
||||||
|
from ironic.conf import xclarity
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@ -76,3 +77,4 @@ redfish.register_opts(CONF)
|
|||||||
service_catalog.register_opts(CONF)
|
service_catalog.register_opts(CONF)
|
||||||
snmp.register_opts(CONF)
|
snmp.register_opts(CONF)
|
||||||
swift.register_opts(CONF)
|
swift.register_opts(CONF)
|
||||||
|
xclarity.register_opts(CONF)
|
||||||
|
@ -61,6 +61,7 @@ _opts = [
|
|||||||
('service_catalog', ironic.conf.service_catalog.list_opts()),
|
('service_catalog', ironic.conf.service_catalog.list_opts()),
|
||||||
('snmp', ironic.conf.snmp.opts),
|
('snmp', ironic.conf.snmp.opts),
|
||||||
('swift', ironic.conf.swift.list_opts()),
|
('swift', ironic.conf.swift.list_opts()),
|
||||||
|
('xclarity', ironic.conf.xclarity.opts),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
33
ironic/conf/xclarity.py
Normal file
33
ironic/conf/xclarity.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# 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 XClarity controller.')),
|
||||||
|
cfg.StrOpt('username',
|
||||||
|
help=_('Username to access the XClarity controller.')),
|
||||||
|
cfg.StrOpt('password',
|
||||||
|
help=_('Password for XClarity controller username.')),
|
||||||
|
cfg.PortOpt('port',
|
||||||
|
default=443,
|
||||||
|
help=_('Port to be used for XClarity operations.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_opts(opts, group='xclarity')
|
0
ironic/drivers/modules/xclarity/__init__.py
Normal file
0
ironic/drivers/modules/xclarity/__init__.py
Normal file
138
ironic/drivers/modules/xclarity/common.py
Normal file
138
ironic/drivers/modules/xclarity/common.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# 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.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_hardware_id': _("XClarity Server Hardware ID. "
|
||||||
|
"Required in driver_info."),
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMON_PROPERTIES = {
|
||||||
|
'xclarity_address': _("IP address of the XClarity node."),
|
||||||
|
'xclarity_username': _("Username for the XClarity with administrator "
|
||||||
|
"privileges."),
|
||||||
|
'xclarity_password': _("Password for xclarity_username."),
|
||||||
|
'xclarity_port': _("Port to be used for xclarity_username."),
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
||||||
|
|
||||||
|
|
||||||
|
def get_properties():
|
||||||
|
return COMMON_PROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
def get_xclarity_client():
|
||||||
|
"""Generates an instance of the XClarity client.
|
||||||
|
|
||||||
|
Generates an instance of the XClarity client using the imported
|
||||||
|
xclarity_client library.
|
||||||
|
|
||||||
|
:returns: an instance of the XClarity client
|
||||||
|
:raises: XClarityError if can't get to the XClarity client
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
xclarity_client = client.Client(
|
||||||
|
ip=CONF.xclarity.manager_ip,
|
||||||
|
username=CONF.xclarity.username,
|
||||||
|
password=CONF.xclarity.password,
|
||||||
|
port=CONF.xclarity.port
|
||||||
|
)
|
||||||
|
except xclarity_client_exceptions.XClarityError as exc:
|
||||||
|
msg = (_("Error getting connection to XClarity manager IP: %(ip)s. "
|
||||||
|
"Error: %(exc)s"), {'ip': CONF.xclarity.manager_ip,
|
||||||
|
'exc': exc})
|
||||||
|
raise XClarityError(error=msg)
|
||||||
|
return xclarity_client
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_hardware_id(node):
|
||||||
|
"""Validates node configuration and returns xclarity hardware id.
|
||||||
|
|
||||||
|
Validates whether node configutation 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(error=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]
|
||||||
|
|
||||||
|
|
||||||
|
def is_node_managed_by_xclarity(xclarity_client, node):
|
||||||
|
"""Determines whether dynamic allocation is enabled for a specifc node.
|
||||||
|
|
||||||
|
:param: xclarity_client: an instance of the XClarity client
|
||||||
|
:param: node: node object to get information from
|
||||||
|
:returns: Boolean depending on whether node is managed by XClarity
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
hardware_id = get_server_hardware_id(node)
|
||||||
|
return xclarity_client.is_node_managed(hardware_id)
|
||||||
|
except exception.MissingParameterValue:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class XClarityError(exception.IronicException):
|
||||||
|
_msg_fmt = _("XClarity exception occurred. Error: %(error)s")
|
219
ironic/drivers/modules/xclarity/management.py
Normal file
219
ironic/drivers/modules/xclarity/management.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# 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'
|
||||||
|
}
|
||||||
|
|
||||||
|
SUPPORTED_BOOT_DEVICES = [
|
||||||
|
boot_devices.PXE,
|
||||||
|
boot_devices.DISK,
|
||||||
|
boot_devices.CDROM,
|
||||||
|
boot_devices.BIOS,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class XClarityManagement(base.ManagementInterface):
|
||||||
|
def __init__(self):
|
||||||
|
super(XClarityManagement, self).__init__()
|
||||||
|
self.xclarity_client = common.get_xclarity_client()
|
||||||
|
|
||||||
|
def get_properties(self):
|
||||||
|
return common.COMMON_PROPERTIES
|
||||||
|
|
||||||
|
@METRICS.timer('XClarityManagement.validate')
|
||||||
|
def validate(self, task):
|
||||||
|
"""It validates if the node is being used by XClarity.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
"""
|
||||||
|
common.is_node_managed_by_xclarity(self.xclarity_client, 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, one of [PXE, DISK, CDROM, BIOS]
|
||||||
|
:raises: InvalidParameterValue if the boot device is not supported.
|
||||||
|
"""
|
||||||
|
if boot_device not in SUPPORTED_BOOT_DEVICES:
|
||||||
|
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
|
||||||
|
:raises: InvalidParameterValue if the boot device is unknown
|
||||||
|
:raises: XClarityError if the communication with XClarity fails
|
||||||
|
"""
|
||||||
|
server_hardware_id = common.get_server_hardware_id(task.node)
|
||||||
|
try:
|
||||||
|
boot_info = (
|
||||||
|
self.xclarity_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': task.node.uuid,
|
||||||
|
'error': xclarity_exc})
|
||||||
|
raise common.XClarityError(error=xclarity_exc)
|
||||||
|
|
||||||
|
persistent = False
|
||||||
|
primary = None
|
||||||
|
boot_order = boot_info['bootOrder']['bootOrderList']
|
||||||
|
for item in boot_order:
|
||||||
|
current = item.get('currentBootOrderDevices', None)
|
||||||
|
boot_type = item.get('bootType', None)
|
||||||
|
if boot_type == "SingleUse":
|
||||||
|
persistent = False
|
||||||
|
primary = current[0]
|
||||||
|
if primary != 'None':
|
||||||
|
boot_device = {'boot_device': primary,
|
||||||
|
'persistent': persistent}
|
||||||
|
self._validate_whether_supported_boot_device(primary)
|
||||||
|
return boot_device
|
||||||
|
elif boot_type == "Permanent":
|
||||||
|
persistent = True
|
||||||
|
boot_device = {'boot_device': current[0],
|
||||||
|
'persistent': persistent}
|
||||||
|
self._validate_supported_boot_device(task, primary)
|
||||||
|
return boot_device
|
||||||
|
|
||||||
|
@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
|
||||||
|
"""
|
||||||
|
self._validate_supported_boot_device(task=task, boot_device=device)
|
||||||
|
|
||||||
|
server_hardware_id = task.node.driver_info.get('server_hardware_id')
|
||||||
|
LOG.debug("Setting boot device to %(device)s for node %(node)s",
|
||||||
|
{"device": device, "node": task.node.uuid})
|
||||||
|
self._set_boot_device(task, server_hardware_id, 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
|
||||||
|
"""
|
||||||
|
boot_info = self.xclarity_client.get_node_all_boot_info(
|
||||||
|
server_hardware_id)
|
||||||
|
xclarity_boot_device = self._translate_ironic_to_xclarity(
|
||||||
|
new_primary_boot_device)
|
||||||
|
current = []
|
||||||
|
LOG.debug(
|
||||||
|
("Setting boot device to %(device)s for XClarity "
|
||||||
|
"node %(node)s"),
|
||||||
|
{'device': xclarity_boot_device, 'node': task.node.uuid}
|
||||||
|
)
|
||||||
|
for item in boot_info['bootOrder']['bootOrderList']:
|
||||||
|
if singleuse and item['bootType'] == 'SingleUse':
|
||||||
|
item['currentBootOrderDevices'][0] = xclarity_boot_device
|
||||||
|
elif not singleuse and item['bootType'] == 'Permanent':
|
||||||
|
current = item['currentBootOrderDevices']
|
||||||
|
if xclarity_boot_device == current[0]:
|
||||||
|
return
|
||||||
|
if xclarity_boot_device in current:
|
||||||
|
current.remove(xclarity_boot_device)
|
||||||
|
current.insert(0, xclarity_boot_device)
|
||||||
|
item['currentBootOrderDevices'] = current
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.xclarity_client.set_node_boot_info(server_hardware_id,
|
||||||
|
boot_info,
|
||||||
|
xclarity_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': xclarity_boot_device, 'node': task.node.uuid,
|
||||||
|
'error': xclarity_exc}
|
||||||
|
)
|
||||||
|
raise common.XClarityError(error=xclarity_exc)
|
112
ironic/drivers/modules/xclarity/power.py
Normal file
112
ironic/drivers/modules/xclarity/power.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# 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 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):
|
||||||
|
def __init__(self):
|
||||||
|
super(XClarityPower, self).__init__()
|
||||||
|
self.xclarity_client = common.get_xclarity_client()
|
||||||
|
|
||||||
|
def get_properties(self):
|
||||||
|
return common.get_properties()
|
||||||
|
|
||||||
|
@METRICS.timer('XClarityPower.validate')
|
||||||
|
def validate(self, task):
|
||||||
|
"""It validates if the node is being used by XClarity.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
common.is_node_managed_by_xclarity(self.xclarity_client, 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
|
||||||
|
"""
|
||||||
|
server_hardware_id = common.get_server_hardware_id(task.node)
|
||||||
|
try:
|
||||||
|
power_state = self.xclarity_client.get_node_power_status(
|
||||||
|
server_hardware_id)
|
||||||
|
except xclarity_client_exceptions.XClarityException as xclarity_exc:
|
||||||
|
LOG.error(
|
||||||
|
("Error getting power state for node %(node)s. Error: "
|
||||||
|
"%(error)s"),
|
||||||
|
{'node': task.node.uuid, 'error': xclarity_exc}
|
||||||
|
)
|
||||||
|
raise common.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):
|
||||||
|
"""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`.
|
||||||
|
:raises: InvalidParameterValue if an invalid power state was specified.
|
||||||
|
:raises: XClarityError if XClarity fails setting the power state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
server_hardware_id = common.get_server_hardware_id(task.node)
|
||||||
|
LOG.debug("Setting power state of node %(node_uuid)s to "
|
||||||
|
"%(power_state)s",
|
||||||
|
{'node_uuid': task.node.uuid, 'power_state': power_state})
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.xclarity_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 common.XClarityError(error=xclarity_exc)
|
||||||
|
|
||||||
|
@METRICS.timer('XClarityPower.reboot')
|
||||||
|
@task_manager.require_exclusive_lock
|
||||||
|
def reboot(self, task):
|
||||||
|
"""Reboot the node
|
||||||
|
|
||||||
|
:param task: a TaskManager instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.set_power_state(task, states.REBOOT)
|
35
ironic/drivers/xclarity.py
Normal file
35
ironic/drivers/xclarity.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# 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]
|
@ -491,6 +491,21 @@ def create_test_node_tag(**kw):
|
|||||||
return dbapi.add_node_tag(tag['node_id'], tag['tag'])
|
return dbapi.add_node_tag(tag['node_id'], tag['tag'])
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_xclarity_properties():
|
||||||
|
return {
|
||||||
|
"cpu_arch": "x86_64",
|
||||||
|
"cpus": "8",
|
||||||
|
"local_gb": "10",
|
||||||
|
"memory_mb": "4096",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_xclarity_driver_info():
|
||||||
|
return {
|
||||||
|
'xclarity_hardware_id': 'fake_sh_id',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_test_node_trait(**kw):
|
def get_test_node_trait(**kw):
|
||||||
return {
|
return {
|
||||||
# TODO(mgoddard): Replace None below with the NodeTrait RPC object
|
# TODO(mgoddard): Replace None below with the NodeTrait RPC object
|
||||||
|
65
ironic/tests/unit/drivers/modules/xclarity/test_common.py
Normal file
65
ironic/tests/unit/drivers/modules/xclarity/test_common.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# 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 mock
|
||||||
|
|
||||||
|
from oslo_utils import importutils
|
||||||
|
|
||||||
|
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_exceptions = importutils.try_import('xclarity_client.exceptions')
|
||||||
|
xclarity_constants = importutils.try_import('xclarity_client.constants')
|
||||||
|
|
||||||
|
|
||||||
|
class XClarityCommonTestCase(db_base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(XClarityCommonTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.config(manager_ip='1.2.3.4', group='xclarity')
|
||||||
|
self.config(username='user', group='xclarity')
|
||||||
|
self.config(password='password', group='xclarity')
|
||||||
|
|
||||||
|
self.node = obj_utils.create_test_node(
|
||||||
|
self.context, driver='fake-xclarity',
|
||||||
|
properties=db_utils.get_test_xclarity_properties(),
|
||||||
|
driver_info=db_utils.get_test_xclarity_driver_info(),
|
||||||
|
)
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
@mock.patch.object(common, 'get_server_hardware_id',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(common, 'get_xclarity_client',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_check_node_managed_by_xclarity(self, mock_xc_client,
|
||||||
|
mock_validate_driver_info):
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
driver_info['xclarity_hardware_id'] = 'abcd'
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
|
||||||
|
xclarity_client = mock_xc_client()
|
||||||
|
mock_validate_driver_info.return_value = '12345'
|
||||||
|
common.is_node_managed_by_xclarity(xclarity_client,
|
||||||
|
self.node)
|
||||||
|
xclarity_client.is_node_managed.assert_called_once_with('12345')
|
125
ironic/tests/unit/drivers/modules/xclarity/test_management.py
Normal file
125
ironic/tests/unit/drivers/modules/xclarity/test_management.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# 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 sys
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from oslo_utils import importutils
|
||||||
|
|
||||||
|
from ironic.common import boot_devices
|
||||||
|
from ironic.conductor import task_manager
|
||||||
|
from ironic.drivers.modules.xclarity import common
|
||||||
|
from ironic.drivers.modules.xclarity import management
|
||||||
|
from ironic.tests.unit.conductor import mgr_utils
|
||||||
|
from ironic.tests.unit.db import base as db_base
|
||||||
|
from ironic.tests.unit.db import utils as db_utils
|
||||||
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
|
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'])
|
||||||
|
mgr_utils.mock_the_extension_manager(
|
||||||
|
driver='xclarity', namespace='ironic.hardware.types')
|
||||||
|
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.REQUIRED_ON_DRIVER_INFO
|
||||||
|
self.assertItemsEqual(expected,
|
||||||
|
self.node.driver_info)
|
||||||
|
|
||||||
|
@mock.patch.object(management.XClarityManagement, 'get_boot_device',
|
||||||
|
return_value='pxe')
|
||||||
|
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:
|
||||||
|
six.moves.reload_module(
|
||||||
|
sys.modules['ironic.drivers.modules.xclarity'])
|
||||||
|
ex = common.XClarityError('E')
|
||||||
|
mock_get_xc_client.return_value.set_node_boot_info.side_effect = ex
|
||||||
|
self.assertRaises(common.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.assertItemsEqual(
|
||||||
|
expected,
|
||||||
|
task.driver.management.get_supported_boot_devices(task))
|
||||||
|
|
||||||
|
@mock.patch.object(
|
||||||
|
management.XClarityManagement,
|
||||||
|
'get_boot_device',
|
||||||
|
return_value={'boot_device': 'pxe', 'persistent': False})
|
||||||
|
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:
|
||||||
|
six.moves.reload_module(
|
||||||
|
sys.modules['ironic.drivers.modules.xclarity'])
|
||||||
|
ex = common.XClarityError('E')
|
||||||
|
mock_xc_client.return_value.get_node_all_boot_info.side_effect = ex
|
||||||
|
self.assertRaises(
|
||||||
|
common.XClarityError,
|
||||||
|
task.driver.management.get_boot_device,
|
||||||
|
task)
|
113
ironic/tests/unit/drivers/modules/xclarity/test_power.py
Normal file
113
ironic/tests/unit/drivers/modules/xclarity/test_power.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
STATE_POWER_ON = "power on"
|
||||||
|
STATE_POWER_OFF = "power off"
|
||||||
|
STATE_POWERING_ON = "power on"
|
||||||
|
STATE_POWERING_OFF = "power on"
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from oslo_utils import importutils
|
||||||
|
|
||||||
|
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.conductor import mgr_utils
|
||||||
|
from ironic.tests.unit.db import base as db_base
|
||||||
|
from ironic.tests.unit.db import utils as db_utils
|
||||||
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
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'])
|
||||||
|
mgr_utils.mock_the_extension_manager(
|
||||||
|
driver='xclarity', namespace='ironic.hardware.types')
|
||||||
|
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.REQUIRED_ON_DRIVER_INFO
|
||||||
|
self.assertItemsEqual(expected,
|
||||||
|
self.node.driver_info)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
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(task)
|
||||||
|
self.assertEqual(STATE_POWER_ON, result)
|
||||||
|
|
||||||
|
def test_get_power_state_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:
|
||||||
|
six.moves.reload_module(
|
||||||
|
sys.modules['ironic.drivers.modules.xclarity'])
|
||||||
|
ex = common.XClarityError('E')
|
||||||
|
mock_xc_client.return_value.get_node_power_status.side_effect = ex
|
||||||
|
self.assertRaises(common.XClarityError,
|
||||||
|
task.driver.power.get_power_state,
|
||||||
|
task)
|
||||||
|
|
||||||
|
@mock.patch.object(power.XClarityPower, 'get_power_state',
|
||||||
|
return_value=states.POWER_ON)
|
||||||
|
def test_set_power(self, mock_set_power_state, 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)
|
||||||
|
|
||||||
|
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:
|
||||||
|
six.moves.reload_module(
|
||||||
|
sys.modules['ironic.drivers.modules.xclarity'])
|
||||||
|
ex = common.XClarityError('E')
|
||||||
|
mock_xc_client.return_value.set_node_power_status.side_effect = ex
|
||||||
|
self.assertRaises(common.XClarityError,
|
||||||
|
task.driver.power.set_power_state,
|
||||||
|
task, states.POWER_OFF)
|
49
ironic/tests/unit/drivers/test_xclarity.py
Normal file
49
ironic/tests/unit/drivers/test_xclarity.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# 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 iscsi_deploy
|
||||||
|
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,
|
||||||
|
iscsi_deploy.ISCSIDeploy,
|
||||||
|
agent.AgentDeploy)
|
||||||
|
self.assertIsInstance(task.driver.management,
|
||||||
|
xc_management.XClarityManagement)
|
||||||
|
self.assertIsInstance(task.driver.power,
|
||||||
|
xc_power.XClarityPower)
|
@ -180,3 +180,21 @@ SUSHY_CONSTANTS_SPEC = (
|
|||||||
'BOOT_SOURCE_ENABLED_CONTINUOUS',
|
'BOOT_SOURCE_ENABLED_CONTINUOUS',
|
||||||
'BOOT_SOURCE_ENABLED_ONCE',
|
'BOOT_SOURCE_ENABLED_ONCE',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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',
|
||||||
|
)
|
||||||
|
@ -270,3 +270,21 @@ if not sushy:
|
|||||||
if 'ironic.drivers.modules.redfish' in sys.modules:
|
if 'ironic.drivers.modules.redfish' in sys.modules:
|
||||||
six.moves.reload_module(
|
six.moves.reload_module(
|
||||||
sys.modules['ironic.drivers.modules.redfish'])
|
sys.modules['ironic.drivers.modules.redfish'])
|
||||||
|
|
||||||
|
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
|
||||||
|
9
releasenotes/notes/xclarity-driver-622800d17459e3f9.yaml
Normal file
9
releasenotes/notes/xclarity-driver-622800d17459e3f9.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds the new ``xclarity`` hardware type for managing Lenovo server
|
||||||
|
hardware with the following interfaces:
|
||||||
|
|
||||||
|
* management: ``xclarity``
|
||||||
|
* power: ``xclarity``
|
@ -127,6 +127,7 @@ ironic.hardware.interfaces.management =
|
|||||||
oneview = ironic.drivers.modules.oneview.management:OneViewManagement
|
oneview = ironic.drivers.modules.oneview.management:OneViewManagement
|
||||||
redfish = ironic.drivers.modules.redfish.management:RedfishManagement
|
redfish = ironic.drivers.modules.redfish.management:RedfishManagement
|
||||||
ucsm = ironic.drivers.modules.ucs.management:UcsManagement
|
ucsm = ironic.drivers.modules.ucs.management:UcsManagement
|
||||||
|
xclarity = ironic.drivers.modules.xclarity.management:XClarityManagement
|
||||||
|
|
||||||
ironic.hardware.interfaces.network =
|
ironic.hardware.interfaces.network =
|
||||||
flat = ironic.drivers.modules.network.flat:FlatNetwork
|
flat = ironic.drivers.modules.network.flat:FlatNetwork
|
||||||
@ -144,6 +145,7 @@ ironic.hardware.interfaces.power =
|
|||||||
redfish = ironic.drivers.modules.redfish.power:RedfishPower
|
redfish = ironic.drivers.modules.redfish.power:RedfishPower
|
||||||
snmp = ironic.drivers.modules.snmp:SNMPPower
|
snmp = ironic.drivers.modules.snmp:SNMPPower
|
||||||
ucsm = ironic.drivers.modules.ucs.power:Power
|
ucsm = ironic.drivers.modules.ucs.power:Power
|
||||||
|
xclarity = ironic.drivers.modules.xclarity.power:XClarityPower
|
||||||
|
|
||||||
ironic.hardware.interfaces.raid =
|
ironic.hardware.interfaces.raid =
|
||||||
agent = ironic.drivers.modules.agent:AgentRAID
|
agent = ironic.drivers.modules.agent:AgentRAID
|
||||||
@ -178,6 +180,7 @@ ironic.hardware.types =
|
|||||||
oneview = ironic.drivers.oneview:OneViewHardware
|
oneview = ironic.drivers.oneview:OneViewHardware
|
||||||
redfish = ironic.drivers.redfish:RedfishHardware
|
redfish = ironic.drivers.redfish:RedfishHardware
|
||||||
snmp = ironic.drivers.snmp:SNMPHardware
|
snmp = ironic.drivers.snmp:SNMPHardware
|
||||||
|
xclarity = ironic.drivers.xclarity:XClarityHardware
|
||||||
|
|
||||||
ironic.database.migration_backend =
|
ironic.database.migration_backend =
|
||||||
sqlalchemy = ironic.db.sqlalchemy.migration
|
sqlalchemy = ironic.db.sqlalchemy.migration
|
||||||
|
Loading…
Reference in New Issue
Block a user