Merge "DRAC: switch to python-dracclient on management interface"

This commit is contained in:
Jenkins 2016-01-04 19:41:14 +00:00 committed by Gerrit Code Review
commit 71dc9bd905
5 changed files with 466 additions and 575 deletions

View File

@ -0,0 +1,53 @@
#
# 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.
"""
DRAC Lifecycle job specific methods
"""
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.i18n import _LE
from ironic.drivers.modules.drac import common as drac_common
drac_exceptions = importutils.try_import('dracclient.exceptions')
LOG = logging.getLogger(__name__)
def validate_job_queue(node):
"""Validates the job queue on the node.
It raises an exception if an unfinished configuration job exists.
:param node: an ironic node object.
:raises: DracOperationError on an error from python-dracclient.
"""
client = drac_common.get_drac_client(node)
try:
unfinished_jobs = client.list_jobs(only_unfinished=True)
except drac_exceptions.BaseClientException as exc:
LOG.error(_LE('DRAC driver failed to get the list of unfinished jobs '
'for node %(node_uuid)s. Reason: %(error)s.'),
{'node_uuid': node.uuid,
'error': exc})
raise exception.DracOperationError(error=exc)
if unfinished_jobs:
msg = _('Unfinished config jobs found: %(jobs)r. Make sure they are '
'completed before retrying.') % {'jobs': unfinished_jobs}
raise exception.DracOperationError(error=msg)

View File

@ -16,7 +16,7 @@
# under the License.
"""
DRAC Management Driver
DRAC management interface
"""
from oslo_log import log as logging
@ -25,14 +25,17 @@ from oslo_utils import importutils
from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common.i18n import _LE
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers.modules.drac import client as drac_client
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import job as drac_job
from ironic.drivers.modules.drac import resource_uris
pywsman = importutils.try_import('pywsman')
drac_exceptions = importutils.try_import('dracclient.exceptions')
LOG = logging.getLogger(__name__)
@ -45,186 +48,79 @@ _BOOT_DEVICES_MAP = {
TARGET_DEVICE = 'BIOS.Setup.1-1'
# RebootJobType constants
_GRACEFUL_REBOOT_WITH_FORCED_SHUTDOWN = '3'
# IsNext constants
PERSISTENT = '1'
""" Is the next boot config the system will use. """
ONE_TIME_BOOT = '3'
""" Is the next boot config the system will use, one time boot only. """
# BootMode constants
PERSISTENT_BOOT_MODE = 'IPL'
NON_PERSISTENT_BOOT_MODE = 'OneTime'
def _get_boot_device(node, controller_version=None):
if controller_version is None:
controller_version = _get_lifecycle_controller_version(node)
def _get_boot_device(node, drac_boot_devices=None):
client = drac_common.get_drac_client(node)
boot_list = _get_next_boot_list(node)
persistent = boot_list['is_next'] == PERSISTENT
boot_list_id = boot_list['instance_id']
try:
boot_modes = client.list_boot_modes()
next_boot_modes = [mode.id for mode in boot_modes if mode.is_next]
if NON_PERSISTENT_BOOT_MODE in next_boot_modes:
next_boot_mode = NON_PERSISTENT_BOOT_MODE
else:
next_boot_mode = next_boot_modes[0]
boot_device_id = _get_boot_device_for_boot_list(node, boot_list_id,
controller_version)
boot_device = next((key for (key, value) in _BOOT_DEVICES_MAP.items()
if value in boot_device_id), None)
return {'boot_device': boot_device, 'persistent': persistent}
if drac_boot_devices is None:
drac_boot_devices = client.list_boot_devices()
drac_boot_device = drac_boot_devices[next_boot_mode][0]
boot_device = next(key for (key, value) in _BOOT_DEVICES_MAP.items()
if value in drac_boot_device.id)
return {'boot_device': boot_device,
'persistent': next_boot_mode == PERSISTENT_BOOT_MODE}
except (drac_exceptions.BaseClientException, IndexError) as exc:
LOG.error(_LE('DRAC driver failed to get next boot mode for '
'node %(node_uuid)s. Reason: %(error)s.'),
{'node_uuid': node.uuid, 'error': exc})
raise exception.DracOperationError(error=exc)
def _get_next_boot_list(node):
"""Get the next boot list.
def set_boot_device(node, device, persistent=False):
"""Set the boot device for a node.
The DCIM_BootConfigSetting resource represents each boot list (eg.
IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot).
The DCIM_BootSourceSetting resource represents each of the boot list boot
devices or sources that are shown under their corresponding boot list.
Set the boot device to use on next boot of the node.
:param node: an ironic node object.
:raises: DracClientError on an error from pywsman library.
:returns: a dictionary containing:
:instance_id: the instance id of the boot list.
:is_next: whether it's the next device to boot or not. One of
PERSISTENT, ONE_TIME_BOOT constants.
:param device: the boot device, one of
:mod:`ironic.common.boot_devices`.
:param persistent: Boolean value. True if the boot device will
persist to all future boots, False if not.
Default: False.
:raises: DracOperationError on an error from python-dracclient.
"""
client = drac_client.get_wsman_client(node)
filter_query = ('select * from DCIM_BootConfigSetting where IsNext=%s '
'or IsNext=%s' % (PERSISTENT, ONE_TIME_BOOT))
try:
doc = client.wsman_enumerate(resource_uris.DCIM_BootConfigSetting,
filter_query=filter_query)
except exception.DracClientError as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DRAC driver failed to get next boot mode for '
'node %(node_uuid)s. Reason: %(error)s.'),
{'node_uuid': node.uuid, 'error': exc})
items = drac_common.find_xml(doc, 'DCIM_BootConfigSetting',
resource_uris.DCIM_BootConfigSetting,
find_all=True)
drac_job.validate_job_queue(node)
# This list will have 2 items maximum, one for the persistent element
# and another one for the OneTime if set
boot_list = None
for i in items:
boot_list_id = drac_common.find_xml(
i, 'InstanceID', resource_uris.DCIM_BootConfigSetting).text
is_next = drac_common.find_xml(
i, 'IsNext', resource_uris.DCIM_BootConfigSetting).text
client = drac_common.get_drac_client(node)
drac_boot_devices = client.list_boot_devices()
boot_list = {'instance_id': boot_list_id, 'is_next': is_next}
# If OneTime is set we should return it, because that's
# where the next boot device is
if is_next == ONE_TIME_BOOT:
break
current_boot_device = _get_boot_device(node, drac_boot_devices)
# If we are already booting from the right device, do nothing.
if current_boot_device == {'boot_device': device,
'persistent': persistent}:
LOG.debug('DRAC already set to boot from %s', device)
return
return boot_list
drac_boot_device = next(drac_device.id for drac_device
in drac_boot_devices[PERSISTENT_BOOT_MODE]
if _BOOT_DEVICES_MAP[device] in drac_device.id)
def _get_boot_device_for_boot_list(node, boot_list_id, controller_version):
"""Get the next boot device for a given boot list.
The DCIM_BootConfigSetting resource represents each boot list (eg.
IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot).
The DCIM_BootSourceSetting resource represents each of the boot list boot
devices or sources that are shown under their corresponding boot list.
:param node: ironic node object.
:param boot_list_id: boot list id.
:param controller_version: version of the Lifecycle controller.
:raises: DracClientError on an error from pywsman library.
:returns: boot device id.
"""
client = drac_client.get_wsman_client(node)
if controller_version < '2.0.0':
filter_query = ('select * from DCIM_BootSourceSetting where '
'PendingAssignedSequence=0')
if persistent:
boot_list = PERSISTENT_BOOT_MODE
else:
filter_query = ('select * from DCIM_BootSourceSetting where '
'PendingAssignedSequence=0 and '
'BootSourceType="%s"' % boot_list_id)
try:
doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting,
filter_query=filter_query)
except exception.DracClientError as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DRAC driver failed to get the current boot '
'device for node %(node_uuid)s. '
'Reason: %(error)s.'),
{'node_uuid': node.uuid, 'error': exc})
boot_list = NON_PERSISTENT_BOOT_MODE
if controller_version < '2.0.0':
boot_devices = drac_common.find_xml(
doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting,
find_all=True)
for device in boot_devices:
if device.text.startswith(boot_list_id):
boot_device_id = device.text
break
else:
boot_device_id = drac_common.find_xml(
doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text
return boot_device_id
def _get_boot_list_for_boot_device(node, device, controller_version):
"""Get the boot list for a given boot device.
The DCIM_BootConfigSetting resource represents each boot list (eg.
IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot).
The DCIM_BootSourceSetting resource represents each of the boot list boot
devices or sources that are shown under their corresponding boot list.
:param node: ironic node object.
:param device: boot device.
:param controller_version: version of the Lifecycle controller.
:raises: DracClientError on an error from pywsman library.
:returns: dictionary containing:
:boot_list: boot list.
:boot_device_id: boot device id.
"""
client = drac_client.get_wsman_client(node)
if controller_version < '2.0.0':
filter_query = None
else:
filter_query = ("select * from DCIM_BootSourceSetting where "
"InstanceID like '%%#%s%%'" %
_BOOT_DEVICES_MAP[device])
try:
doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting,
filter_query=filter_query)
except exception.DracClientError as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DRAC driver failed to set the boot device '
'for node %(node_uuid)s. Can\'t find the ID '
'for the %(device)s type. Reason: %(error)s.'),
{'node_uuid': node.uuid, 'error': exc,
'device': device})
if controller_version < '2.0.0':
boot_devices = drac_common.find_xml(
doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting,
find_all=True)
for boot_device in boot_devices:
if _BOOT_DEVICES_MAP[device] in boot_device.text:
boot_device_id = boot_device.text
boot_list = boot_device_id.split(':')[0]
break
else:
boot_device_id = drac_common.find_xml(
doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text
boot_list = drac_common.find_xml(
doc, 'BootSourceType', resource_uris.DCIM_BootSourceSetting).text
return {'boot_list': boot_list, 'boot_device_id': boot_device_id}
client.change_boot_device_order(boot_list, drac_boot_device)
client.commit_pending_bios_changes()
# TODO(ifarkas): delete this during BIOS vendor_passthru refactor
def create_config_job(node, reboot=False):
"""Create a configuration job.
@ -263,6 +159,7 @@ def create_config_job(node, reboot=False):
{'node_uuid': node.uuid, 'error': exc})
# TODO(ifarkas): delete this during BIOS vendor_passthru refactor
def check_for_config_job(node):
"""Check if a configuration job is already created.
@ -303,33 +200,10 @@ def check_for_config_job(node):
target=TARGET_DEVICE)
def _get_lifecycle_controller_version(node):
"""Returns the Lifecycle controller version of the DRAC card of the node
:param node: the node.
:returns: the Lifecycle controller version.
:raises: DracClientError if the client received unexpected response.
:raises: InvalidParameterValue if required DRAC credentials are missing.
"""
client = drac_client.get_wsman_client(node)
filter_query = ('select LifecycleControllerVersion from DCIM_SystemView')
try:
doc = client.wsman_enumerate(resource_uris.DCIM_SystemView,
filter_query=filter_query)
except exception.DracClientError as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DRAC driver failed to get power state for node '
'%(node_uuid)s. Reason: %(error)s.'),
{'node_uuid': node.uuid, 'error': exc})
version = drac_common.find_xml(doc, 'LifecycleControllerVersion',
resource_uris.DCIM_SystemView).text
return version
class DracManagement(base.ManagementInterface):
def get_properties(self):
"""Return the properties of the interface."""
return drac_common.COMMON_PROPERTIES
def validate(self, task):
@ -349,93 +223,70 @@ class DracManagement(base.ManagementInterface):
def get_supported_boot_devices(self, task):
"""Get a list of the supported boot devices.
:param task: a task from TaskManager.
:param task: a TaskManager instance containing the node to act on.
:returns: A list with the supported boot devices defined
in :mod:`ironic.common.boot_devices`.
"""
return list(_BOOT_DEVICES_MAP.keys())
def get_boot_device(self, task):
"""Get the current boot device for a node.
Returns the current boot device of the node.
:param task: a TaskManager instance containing the node to act on.
:raises: DracOperationError on an error from python-dracclient.
:returns: a dictionary containing:
:boot_device: the boot device, one of
:mod:`ironic.common.boot_devices` or None if it is unknown.
:persistent: whether the boot device will persist to all
future boots or not, None if it is unknown.
"""
node = task.node
if ('drac_boot_device' in node.driver_internal_info and
node.driver_internal_info['drac_boot_device'] is not None):
return node.driver_internal_info['drac_boot_device']
else:
return _get_boot_device(node)
@task_manager.require_exclusive_lock
def set_boot_device(self, task, device, persistent=False):
"""Set the boot device for a node.
Set the boot device to use on next reboot of the node.
:param task: a task from TaskManager.
:param task: a TaskManager instance containing the node to act on.
:param device: the boot device, one of
:mod:`ironic.common.boot_devices`.
:param persistent: Boolean value. True if the boot device will
persist to all future boots, False if not.
Default: False.
:raises: DracClientError if the client received unexpected response.
:raises: DracOperationFailed if the client received response with an
error message.
:raises: DracUnexpectedReturnValue if the client received a response
with unexpected return value.
:raises: InvalidParameterValue if an invalid boot device is
specified.
:raises: DracPendingConfigJobExists on an error when creating the job.
:raises: InvalidParameterValue if an invalid boot device is specified.
"""
node = task.node
client = drac_client.get_wsman_client(task.node)
controller_version = _get_lifecycle_controller_version(task.node)
current_boot_device = _get_boot_device(task.node, controller_version)
if device not in _BOOT_DEVICES_MAP:
raise exception.InvalidParameterValue(
_("set_boot_device called with invalid device '%(device)s' "
"for node %(node_id)s.") % {'device': device,
'node_id': node.uuid})
# If we are already booting from the right device, do nothing.
if current_boot_device == {'boot_device': device,
'persistent': persistent}:
LOG.debug('DRAC already set to boot from %s', device)
return
# Check for an existing configuration job
check_for_config_job(task.node)
# Querying the boot device attributes
boot_device = _get_boot_list_for_boot_device(task.node, device,
controller_version)
boot_list = boot_device['boot_list']
boot_device_id = boot_device['boot_device_id']
if not persistent:
boot_list = 'OneTime'
# Send the request to DRAC
selectors = {'InstanceID': boot_list}
properties = {'source': boot_device_id}
try:
client.wsman_invoke(resource_uris.DCIM_BootConfigSetting,
'ChangeBootOrderByInstanceID', selectors,
properties, drac_client.RET_SUCCESS)
except exception.DracRequestFailed as exc:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DRAC driver failed to set the boot device for '
'node %(node_uuid)s to %(target_boot_device)s. '
'Reason: %(error)s.'),
{'node_uuid': task.node.uuid,
'target_boot_device': device,
'error': exc})
# Create a configuration job
create_config_job(task.node)
def get_boot_device(self, task):
"""Get the current boot device for a node.
Returns the current boot device of the node.
:param task: a task from TaskManager.
:raises: DracClientError on an error from pywsman library.
:returns: a dictionary containing:
:boot_device: the boot device, one of
:mod:`ironic.common.boot_devices` or None if it is unknown.
:persistent: Whether the boot device will persist to all
future boots or not, None if it is unknown.
"""
return _get_boot_device(task.node)
# NOTE(ifarkas): DRAC interface doesn't allow changing the boot device
# multiple times in a row without a reboot. This is
# because a change need to be committed via a
# configuration job, and further configuration jobs
# cannot be created until the previous one is processed
# at the next boot. As a workaround, saving it to
# driver_internal_info and committing the change during
# power state change.
driver_internal_info = node.driver_internal_info
driver_internal_info['drac_boot_device'] = {'boot_device': device,
'persistent': persistent}
node.driver_internal_info = driver_internal_info
node.save()
def get_sensors_data(self, task):
"""Get sensors data.

View File

@ -24,6 +24,7 @@ from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import management as drac_management
drac_constants = importutils.try_import('dracclient.constants')
drac_exceptions = importutils.try_import('dracclient.exceptions')
@ -62,6 +63,20 @@ def _get_power_state(node):
return POWER_STATES[drac_power_state]
def _commit_boot_list_change(node):
driver_internal_info = node.driver_internal_info
if ('drac_boot_device' in driver_internal_info and
driver_internal_info['drac_boot_device'] is not None):
boot_device = driver_internal_info['drac_boot_device']
drac_management.set_boot_device(node, boot_device['boot_device'],
boot_device['persistent'])
driver_internal_info['drac_boot_device'] = None
node.driver_internal_info = driver_internal_info
node.save()
def _set_power_state(node, power_state):
"""Turns the server power on/off or do a reboot.
@ -71,6 +86,8 @@ def _set_power_state(node, power_state):
:raises: DracOperationError on an error from python-dracclient
"""
_commit_boot_list_change(node)
client = drac_common.get_drac_client(node)
target_power_state = REVERSE_POWER_STATES[power_state]

View File

@ -0,0 +1,67 @@
#
# 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 DRAC job specific methods
"""
from dracclient import exceptions as drac_exceptions
import mock
from ironic.common import exception
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import job as drac_job
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
INFO_DICT = db_utils.get_test_drac_info()
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
class DracJobTestCase(db_base.DbTestCase):
def setUp(self):
super(DracJobTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_drac')
self.node = obj_utils.create_test_node(self.context,
driver='fake_drac',
driver_info=INFO_DICT)
def test_validate_job_queue(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_jobs.return_value = []
drac_job.validate_job_queue(self.node)
mock_client.list_jobs.assert_called_once_with(only_unfinished=True)
def test_validate_job_queue_fail(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
exc = drac_exceptions.BaseClientException('boom')
mock_client.list_jobs.side_effect = exc
self.assertRaises(exception.DracOperationError,
drac_job.validate_job_queue, self.node)
def test_validate_job_queue_invalid(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_jobs.return_value = [42]
self.assertRaises(exception.DracOperationError,
drac_job.validate_job_queue, self.node)

View File

@ -16,16 +16,17 @@
# under the License.
"""
Test class for DRAC ManagementInterface
Test class for DRAC management interface
"""
import mock
from ironic.common import boot_devices
import ironic.common.boot_devices
from ironic.common import exception
from ironic.conductor import task_manager
from ironic.drivers.modules.drac import client as drac_client
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import job as drac_job
from ironic.drivers.modules.drac import management as drac_mgmt
from ironic.drivers.modules.drac import resource_uris
from ironic.tests.unit.conductor import mgr_utils
@ -39,7 +40,8 @@ from ironic.tests.unit.objects import utils as obj_utils
INFO_DICT = db_utils.get_test_drac_info()
@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
class DracManagementInternalMethodsTestCase(db_base.DbTestCase):
def setUp(self):
@ -48,43 +50,235 @@ class DracManagementInternalMethodsTestCase(db_base.DbTestCase):
self.node = obj_utils.create_test_node(self.context,
driver='fake_drac',
driver_info=INFO_DICT)
self.boot_mode_ipl = {'id': 'IPL', 'name': 'BootSeq',
'is_current': True, 'is_next': True}
self.boot_mode_one_time = {'id': 'OneTime', 'name': 'OneTimeBootMode',
'is_current': False, 'is_next': False}
def test__get_next_boot_list(self, mock_client_pywsman):
result_xml = test_utils.build_soap_xml(
[{'DCIM_BootConfigSetting': {'InstanceID': 'IPL',
'IsNext': drac_mgmt.PERSISTENT}}],
resource_uris.DCIM_BootConfigSetting)
self.boot_device_pxe = {
'id': 'BIOS.Setup.1-1#BootSeq#NIC.Embedded.1-1-1',
'boot_mode': 'IPL',
'current_assigned_sequence': 0,
'pending_assigned_sequence': 0,
'bios_boot_string': 'Embedded NIC 1 Port 1 Partition 1'}
self.boot_device_disk = {
'id': 'BIOS.Setup.1-1#BootSeq#HardDisk.List.1-1',
'boot_mode': 'IPL',
'current_assigned_sequence': 1,
'pending_assigned_sequence': 1,
'bios_boot_string': 'Hard drive C: BootSeq'}
mock_xml = test_utils.mock_wsman_root(result_xml)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml
def test__get_boot_device(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_boot_modes.return_value = [
mock.Mock(**self.boot_mode_ipl),
mock.Mock(**self.boot_mode_one_time)]
mock_client.list_boot_devices.return_value = {
'IPL': [mock.Mock(**self.boot_device_pxe),
mock.Mock(**self.boot_device_disk)]}
expected = {'instance_id': 'IPL', 'is_next': drac_mgmt.PERSISTENT}
result = drac_mgmt._get_next_boot_list(self.node)
boot_device = drac_mgmt._get_boot_device(self.node)
self.assertEqual(expected, result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootConfigSetting)
expected_boot_device = {'boot_device': 'pxe', 'persistent': True}
self.assertEqual(expected_boot_device, boot_device)
mock_client.list_boot_modes.assert_called_once_with()
mock_client.list_boot_devices.assert_called_once_with()
def test__get_next_boot_list_onetime(self, mock_client_pywsman):
result_xml = test_utils.build_soap_xml(
[{'DCIM_BootConfigSetting': {'InstanceID': 'IPL',
'IsNext': drac_mgmt.PERSISTENT}},
{'DCIM_BootConfigSetting': {'InstanceID': 'OneTime',
'IsNext': drac_mgmt.ONE_TIME_BOOT}}],
resource_uris.DCIM_BootConfigSetting)
def test__get_boot_device_not_persistent(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
self.boot_mode_one_time['is_next'] = True
mock_client.list_boot_modes.return_value = [
mock.Mock(**self.boot_mode_ipl),
mock.Mock(**self.boot_mode_one_time)]
mock_client.list_boot_devices.return_value = {
'OneTime': [mock.Mock(**self.boot_device_pxe),
mock.Mock(**self.boot_device_disk)]}
mock_xml = test_utils.mock_wsman_root(result_xml)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml
boot_device = drac_mgmt._get_boot_device(self.node)
expected = {'instance_id': 'OneTime',
'is_next': drac_mgmt.ONE_TIME_BOOT}
result = drac_mgmt._get_next_boot_list(self.node)
expected_boot_device = {'boot_device': 'pxe', 'persistent': False}
self.assertEqual(expected_boot_device, boot_device)
mock_client.list_boot_modes.assert_called_once_with()
mock_client.list_boot_devices.assert_called_once_with()
self.assertEqual(expected, result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootConfigSetting)
def test__get_boot_device_with_empty_boot_mode_list(self,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_boot_modes.return_value = []
self.assertRaises(exception.DracOperationError,
drac_mgmt._get_boot_device, self.node)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True)
def test_set_boot_device(self, mock_validate_job_queue,
mock__get_boot_device, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_boot_modes.return_value = [
mock.Mock(**self.boot_mode_ipl),
mock.Mock(**self.boot_mode_one_time)]
mock_client.list_boot_devices.return_value = {
'IPL': [mock.Mock(**self.boot_device_pxe),
mock.Mock(**self.boot_device_disk)]}
boot_device = {'boot_device': ironic.common.boot_devices.DISK,
'persistent': True}
mock__get_boot_device.return_value = boot_device
boot_device = drac_mgmt.set_boot_device(
self.node, ironic.common.boot_devices.PXE, persistent=False)
mock_client.change_boot_device_order.assert_called_once_with(
'OneTime', 'BIOS.Setup.1-1#BootSeq#NIC.Embedded.1-1-1')
mock_client.commit_pending_bios_changes.assert_called_once_with()
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True)
def test_set_boot_device_called_with_no_change(
self, mock_validate_job_queue, mock__get_boot_device,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_boot_modes.return_value = [
mock.Mock(**self.boot_mode_ipl),
mock.Mock(**self.boot_mode_one_time)]
mock_client.list_boot_devices.return_value = {
'IPL': [mock.Mock(**self.boot_device_pxe),
mock.Mock(**self.boot_device_disk)]}
boot_device = {'boot_device': ironic.common.boot_devices.PXE,
'persistent': True}
mock__get_boot_device.return_value = boot_device
boot_device = drac_mgmt.set_boot_device(
self.node, ironic.common.boot_devices.PXE, persistent=True)
self.assertEqual(0, mock_client.change_boot_device_order.call_count)
self.assertEqual(0, mock_client.commit_pending_bios_changes.call_count)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True)
def test_set_boot_device_with_invalid_job_queue(
self, mock_validate_job_queue, mock__get_boot_device,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_validate_job_queue.side_effect = exception.DracOperationError(
'boom')
self.assertRaises(exception.DracOperationError,
drac_mgmt.set_boot_device, self.node,
ironic.common.boot_devices.PXE, persistent=True)
self.assertEqual(0, mock_client.change_boot_device_order.call_count)
self.assertEqual(0, mock_client.commit_pending_bios_changes.call_count)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
class DracManagementTestCase(db_base.DbTestCase):
def setUp(self):
super(DracManagementTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_drac')
self.node = obj_utils.create_test_node(self.context,
driver='fake_drac',
driver_info=INFO_DICT)
def test_get_properties(self, mock_get_drac_client):
expected = drac_common.COMMON_PROPERTIES
driver = drac_mgmt.DracManagement()
self.assertEqual(expected, driver.get_properties())
def test_get_supported_boot_devices(self, mock_get_drac_client):
expected_boot_devices = [ironic.common.boot_devices.PXE,
ironic.common.boot_devices.DISK,
ironic.common.boot_devices.CDROM]
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
boot_devices = (
task.driver.management.get_supported_boot_devices(task))
self.assertEqual(sorted(expected_boot_devices), sorted(boot_devices))
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
def test_get_boot_device(self, mock__get_boot_device,
mock_get_drac_client):
expected_boot_device = {'boot_device': ironic.common.boot_devices.DISK,
'persistent': True}
mock__get_boot_device.return_value = expected_boot_device
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
boot_device = task.driver.management.get_boot_device(task)
self.assertEqual(expected_boot_device, boot_device)
mock__get_boot_device.assert_called_once_with(task.node)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
def test_get_boot_device_from_driver_internal_info(self,
mock__get_boot_device,
mock_get_drac_client):
expected_boot_device = {'boot_device': ironic.common.boot_devices.DISK,
'persistent': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_internal_info['drac_boot_device'] = (
expected_boot_device)
boot_device = task.driver.management.get_boot_device(task)
self.assertEqual(expected_boot_device, boot_device)
self.assertEqual(0, mock__get_boot_device.call_count)
def test_set_boot_device(self, mock_get_drac_client):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.management.set_boot_device(
task, ironic.common.boot_devices.DISK, persistent=True)
expected_boot_device = {
'boot_device': ironic.common.boot_devices.DISK,
'persistent': True}
self.assertEqual(
task.node.driver_internal_info['drac_boot_device'],
expected_boot_device)
def test_set_boot_device_fail(self, mock_get_drac_client):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.set_boot_device, task,
'foo')
def test_get_sensors_data(self, mock_get_drac_client):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaises(NotImplementedError,
task.driver.management.get_sensors_data, task)
# TODO(ifarkas): delete this during BIOS vendor_passthru refactor
@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
class DracConfigJobMethodsTestCase(db_base.DbTestCase):
def setUp(self):
super(DracConfigJobMethodsTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_drac')
self.node = obj_utils.create_test_node(self.context,
driver='fake_drac',
driver_info=INFO_DICT)
def test__check_for_config_job(self, mock_client_pywsman):
result_xml = test_utils.build_soap_xml(
@ -189,294 +383,3 @@ class DracManagementInternalMethodsTestCase(db_base.DbTestCase):
mock_pywsman.invoke.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BIOSService,
'CreateTargetedConfigJob', None)
def test__get_lifecycle_controller_version(self, mock_client_pywsman):
result_xml = test_utils.build_soap_xml(
[{'DCIM_SystemView': {'LifecycleControllerVersion': '42'}}],
resource_uris.DCIM_SystemView)
mock_xml = test_utils.mock_wsman_root(result_xml)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml
result = drac_mgmt._get_lifecycle_controller_version(self.node)
self.assertEqual('42', result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_SystemView)
@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
class DracManagementTestCase(db_base.DbTestCase):
def setUp(self):
super(DracManagementTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver='fake_drac')
self.node = obj_utils.create_test_node(self.context,
driver='fake_drac',
driver_info=INFO_DICT)
self.driver = drac_mgmt.DracManagement()
self.task = mock.Mock(spec=['node'])
self.task.node = self.node
def test_get_properties(self, mock_client_pywsman):
expected = drac_common.COMMON_PROPERTIES
self.assertEqual(expected, self.driver.get_properties())
def test_get_supported_boot_devices(self, mock_client_pywsman):
expected = [boot_devices.PXE, boot_devices.DISK, boot_devices.CDROM]
self.assertEqual(sorted(expected),
sorted(self.driver.
get_supported_boot_devices(self.task)))
@mock.patch.object(drac_mgmt, '_get_next_boot_list', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
def test_get_boot_device(self, mock_glcv, mock_gnbl, mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gnbl.return_value = {'instance_id': 'OneTime',
'is_next': drac_mgmt.ONE_TIME_BOOT}
result_xml = test_utils.build_soap_xml(
[{'InstanceID': 'HardDisk'}], resource_uris.DCIM_BootSourceSetting)
mock_xml = test_utils.mock_wsman_root(result_xml)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml
result = self.driver.get_boot_device(self.task)
expected = {'boot_device': boot_devices.DISK, 'persistent': False}
self.assertEqual(expected, result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootSourceSetting)
@mock.patch.object(drac_mgmt, '_get_next_boot_list', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
def test_get_boot_device_persistent(self, mock_glcv, mock_gnbl,
mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gnbl.return_value = {'instance_id': 'IPL',
'is_next': drac_mgmt.PERSISTENT}
result_xml = test_utils.build_soap_xml(
[{'InstanceID': 'NIC', 'BootSourceType': 'IPL'}],
resource_uris.DCIM_BootSourceSetting)
mock_xml = test_utils.mock_wsman_root(result_xml)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml
result = self.driver.get_boot_device(self.task)
expected = {'boot_device': boot_devices.PXE, 'persistent': True}
self.assertEqual(expected, result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootSourceSetting)
@mock.patch.object(drac_client.Client, 'wsman_enumerate', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_next_boot_list', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
def test_get_boot_device_client_error(self, mock_glcv, mock_gnbl, mock_we,
mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gnbl.return_value = {'instance_id': 'OneTime',
'is_next': drac_mgmt.ONE_TIME_BOOT}
mock_we.side_effect = iter([exception.DracClientError('E_FAKE')])
self.assertRaises(exception.DracClientError,
self.driver.get_boot_device, self.task)
mock_we.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BootSourceSetting,
filter_query=mock.ANY)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
@mock.patch.object(drac_mgmt, 'check_for_config_job', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, 'create_config_job', spec_set=True,
autospec=True)
def test_set_boot_device(self, mock_ccj, mock_cfcj, mock_glcv, mock_gbd,
mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gbd.return_value = {'boot_device': boot_devices.PXE,
'persistent': True}
result_xml_enum = test_utils.build_soap_xml(
[{'InstanceID': 'NIC', 'BootSourceType': 'IPL'}],
resource_uris.DCIM_BootSourceSetting)
result_xml_invk = test_utils.build_soap_xml(
[{'ReturnValue': drac_client.RET_SUCCESS}],
resource_uris.DCIM_BootConfigSetting)
mock_xml_enum = test_utils.mock_wsman_root(result_xml_enum)
mock_xml_invk = test_utils.mock_wsman_root(result_xml_invk)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml_enum
mock_pywsman.invoke.return_value = mock_xml_invk
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node = self.node
result = self.driver.set_boot_device(task, boot_devices.PXE)
self.assertIsNone(result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootSourceSetting)
mock_pywsman.invoke.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BootConfigSetting,
'ChangeBootOrderByInstanceID', None)
mock_glcv.assert_called_once_with(self.node)
mock_gbd.assert_called_once_with(self.node, controller_version)
mock_cfcj.assert_called_once_with(self.node)
mock_ccj.assert_called_once_with(self.node)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
@mock.patch.object(drac_mgmt, 'check_for_config_job', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, 'create_config_job', spec_set=True,
autospec=True)
def test_set_boot_device_fail(self, mock_ccj, mock_cfcj, mock_glcv,
mock_gbd, mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gbd.return_value = {'boot_device': boot_devices.PXE,
'persistent': True}
result_xml_enum = test_utils.build_soap_xml(
[{'InstanceID': 'NIC', 'BootSourceType': 'IPL'}],
resource_uris.DCIM_BootSourceSetting)
result_xml_invk = test_utils.build_soap_xml(
[{'ReturnValue': drac_client.RET_ERROR, 'Message': 'E_FAKE'}],
resource_uris.DCIM_BootConfigSetting)
mock_xml_enum = test_utils.mock_wsman_root(result_xml_enum)
mock_xml_invk = test_utils.mock_wsman_root(result_xml_invk)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml_enum
mock_pywsman.invoke.return_value = mock_xml_invk
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node = self.node
self.assertRaises(exception.DracOperationFailed,
self.driver.set_boot_device, task,
boot_devices.PXE)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootSourceSetting)
mock_pywsman.invoke.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BootConfigSetting,
'ChangeBootOrderByInstanceID', None)
mock_glcv.assert_called_once_with(self.node)
mock_gbd.assert_called_once_with(self.node, controller_version)
mock_cfcj.assert_called_once_with(self.node)
self.assertFalse(mock_ccj.called)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
@mock.patch.object(drac_client.Client, 'wsman_enumerate', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, 'check_for_config_job', spec_set=True,
autospec=True)
def test_set_boot_device_client_error(self, mock_cfcj, mock_we, mock_glcv,
mock_gbd,
mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gbd.return_value = {'boot_device': boot_devices.PXE,
'persistent': True}
mock_we.side_effect = iter([exception.DracClientError('E_FAKE')])
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node = self.node
self.assertRaises(exception.DracClientError,
self.driver.set_boot_device, task,
boot_devices.PXE)
mock_glcv.assert_called_once_with(self.node)
mock_gbd.assert_called_once_with(self.node, controller_version)
mock_we.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BootSourceSetting,
filter_query=mock.ANY)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
@mock.patch.object(drac_mgmt, 'check_for_config_job', spec_set=True,
autospec=True)
def test_set_boot_device_noop(self, mock_cfcj, mock_glcv, mock_gbd,
mock_client_pywsman):
controller_version = '2.1.5.0'
mock_glcv.return_value = controller_version
mock_gbd.return_value = {'boot_device': boot_devices.PXE,
'persistent': False}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node = self.node
result = self.driver.set_boot_device(task, boot_devices.PXE)
self.assertIsNone(result)
mock_glcv.assert_called_once_with(self.node)
mock_gbd.assert_called_once_with(self.node, controller_version)
self.assertFalse(mock_cfcj.called)
def test_get_sensors_data(self, mock_client_pywsman):
self.assertRaises(NotImplementedError,
self.driver.get_sensors_data, self.task)
@mock.patch.object(drac_mgmt, '_get_boot_device', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, '_get_lifecycle_controller_version',
spec_set=True, autospec=True)
@mock.patch.object(drac_mgmt, 'check_for_config_job', spec_set=True,
autospec=True)
@mock.patch.object(drac_mgmt, 'create_config_job', spec_set=True,
autospec=True)
def test_set_boot_device_11g(self, mock_ccj, mock_cfcj, mock_glcv,
mock_gbd, mock_client_pywsman):
controller_version = '1.5.0.0'
mock_glcv.return_value = controller_version
mock_gbd.return_value = {'boot_device': boot_devices.PXE,
'persistent': True}
result_xml_enum = test_utils.build_soap_xml(
[{'InstanceID': 'NIC'}],
resource_uris.DCIM_BootSourceSetting)
result_xml_invk = test_utils.build_soap_xml(
[{'ReturnValue': drac_client.RET_SUCCESS}],
resource_uris.DCIM_BootConfigSetting)
mock_xml_enum = test_utils.mock_wsman_root(result_xml_enum)
mock_xml_invk = test_utils.mock_wsman_root(result_xml_invk)
mock_pywsman = mock_client_pywsman.Client.return_value
mock_pywsman.enumerate.return_value = mock_xml_enum
mock_pywsman.invoke.return_value = mock_xml_invk
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node = self.node
result = self.driver.set_boot_device(task, boot_devices.PXE)
self.assertIsNone(result)
mock_pywsman.enumerate.assert_called_once_with(
mock.ANY, mock.ANY, resource_uris.DCIM_BootSourceSetting)
mock_pywsman.invoke.assert_called_once_with(
mock.ANY, resource_uris.DCIM_BootConfigSetting,
'ChangeBootOrderByInstanceID', None)
mock_glcv.assert_called_once_with(self.node)
mock_gbd.assert_called_once_with(self.node, controller_version)
mock_cfcj.assert_called_once_with(self.node)
mock_ccj.assert_called_once_with(self.node)