Add iDRAC RAID deploy steps

This change adds two new deploy steps to the idrac RAID interface -
apply_configuration and delete_configuration. These use the existing
RAID support in the idrac driver used for clean steps.

In order to make this work, the lifecycle controller job validation has
been modified to allow specification of a name prefix for jobs to check.
This is because configuring the node for PXE boot can result in creation
of a BIOS config job, which previously caused the validation to fail.
The RAID interface now only checks for existing jobs on the same RAID
controller, and so ignores the BIOS config job.

The disk space calculation has been modified to allow for virtual disks
that are pending deletion, as this is necessary to make the numbers work
when deleting existing virtual disks and creating new ones from the same
set of physical disks.

We also use the new deployment_polling flag in driver_internal_info to
signal that the RAID interface polls for completion of the deploy step.

Co-Authored-By: Shivanand Tendulker <stendulker@gmail.com>
Change-Id: I5803131fbdebce6f7896655a61a8fbdd4c1cd4a1
Story: 2003817
Task: 30004
This commit is contained in:
Shivanand Tendulker 2019-08-02 06:37:05 -04:00 committed by Mark Goddard
parent 3e51982252
commit 45b03d03b7
6 changed files with 478 additions and 109 deletions

View File

@ -27,20 +27,26 @@ drac_exceptions = importutils.try_import('dracclient.exceptions')
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def validate_job_queue(node): def validate_job_queue(node, name_prefix=None):
"""Validates the job queue on the node. """Validates the job queue on the node.
It raises an exception if an unfinished configuration job exists. It raises an exception if an unfinished configuration job exists.
:param node: an ironic node object. :param node: an ironic node object.
:param name_prefix: A name prefix for jobs to validate.
:raises: DracOperationError on an error from python-dracclient. :raises: DracOperationError on an error from python-dracclient.
""" """
unfinished_jobs = list_unfinished_jobs(node) unfinished_jobs = list_unfinished_jobs(node)
if unfinished_jobs: if name_prefix is not None:
msg = _('Unfinished config jobs found: %(jobs)r. Make sure they are ' # Filter out jobs that don't match the name prefix.
'completed before retrying.') % {'jobs': unfinished_jobs} unfinished_jobs = [job for job in unfinished_jobs
raise exception.DracOperationError(error=msg) if job.name.startswith(name_prefix)]
if not unfinished_jobs:
return
msg = _('Unfinished config jobs found: %(jobs)r. Make sure they are '
'completed before retrying.') % {'jobs': unfinished_jobs}
raise exception.DracOperationError(error=msg)
def get_job(node, job_id): def get_job(node, job_id):

View File

@ -26,11 +26,11 @@ from oslo_utils import units
from ironic.common import exception from ironic.common import exception
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common import raid as raid_common from ironic.common import raid as raid_common
from ironic.common import states
from ironic.conductor import task_manager from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils from ironic.conductor import utils as manager_utils
from ironic.conf import CONF from ironic.conf import CONF
from ironic.drivers import base from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.drac import common as drac_common 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 job as drac_job
@ -163,6 +163,18 @@ def _is_raid_controller(node, raid_controller_fqdd, raid_controllers=None):
raise exception.DracOperationError(error=exc) raise exception.DracOperationError(error=exc)
def _validate_job_queue(node, raid_controller=None):
"""Validate that there are no pending jobs for this controller.
:param node: an ironic node object.
:param raid_controller: id of the RAID controller.
"""
kwargs = {}
if raid_controller:
kwargs["name_prefix"] = "Config:RAID:%s" % raid_controller
drac_job.validate_job_queue(node, **kwargs)
def create_virtual_disk(node, raid_controller, physical_disks, raid_level, def create_virtual_disk(node, raid_controller, physical_disks, raid_level,
size_mb, disk_name=None, span_length=None, size_mb, disk_name=None, span_length=None,
span_depth=None): span_depth=None):
@ -185,7 +197,9 @@ def create_virtual_disk(node, raid_controller, physical_disks, raid_level,
values to be applied. values to be applied.
:raises: DracOperationError on an error from python-dracclient. :raises: DracOperationError on an error from python-dracclient.
""" """
drac_job.validate_job_queue(node) # This causes config to fail, because the boot mode is set via a config
# job.
_validate_job_queue(node, raid_controller)
client = drac_common.get_drac_client(node) client = drac_common.get_drac_client(node)
@ -215,7 +229,8 @@ def delete_virtual_disk(node, virtual_disk):
values to be applied. values to be applied.
:raises: DracOperationError on an error from python-dracclient. :raises: DracOperationError on an error from python-dracclient.
""" """
drac_job.validate_job_queue(node) # NOTE(mgoddard): Cannot specify raid_controller as we don't know it.
_validate_job_queue(node)
client = drac_common.get_drac_client(node) client = drac_common.get_drac_client(node)
@ -247,7 +262,7 @@ def _reset_raid_config(node, raid_controller):
""" """
try: try:
drac_job.validate_job_queue(node) _validate_job_queue(node, raid_controller)
client = drac_common.get_drac_client(node) client = drac_common.get_drac_client(node)
return client.reset_raid_config(raid_controller) return client.reset_raid_config(raid_controller)
@ -279,7 +294,7 @@ def clear_foreign_config(node, raid_controller):
""" """
try: try:
drac_job.validate_job_queue(node) _validate_job_queue(node, raid_controller)
client = drac_common.get_drac_client(node) client = drac_common.get_drac_client(node)
return client.clear_foreign_config(raid_controller) return client.clear_foreign_config(raid_controller)
@ -480,13 +495,18 @@ def _volume_usage_per_disk_mb(logical_disk, physical_disks, spans_count=1,
return int(stripes_per_disk * stripe_size_kb / units.Ki) return int(stripes_per_disk * stripe_size_kb / units.Ki)
def _find_configuration(logical_disks, physical_disks): def _find_configuration(logical_disks, physical_disks, pending_delete):
"""Find RAID configuration. """Find RAID configuration.
This method transforms the RAID configuration defined in Ironic to a format This method transforms the RAID configuration defined in Ironic to a format
that is required by dracclient. This includes matching the physical disks that is required by dracclient. This includes matching the physical disks
to RAID volumes when it's not pre-defined, or in general calculating to RAID volumes when it's not pre-defined, or in general calculating
missing properties. missing properties.
:param logical_disks: list of logical disk definitions.
:param physical_disks: list of physical disk definitions.
:param pending_delete: Whether there is a pending deletion of virtual
disks that should be accounted for.
""" """
# shared physical disks of RAID volumes size_gb='MAX' should be # shared physical disks of RAID volumes size_gb='MAX' should be
@ -508,7 +528,7 @@ def _find_configuration(logical_disks, physical_disks):
free_space_mb = {} free_space_mb = {}
for disk in physical_disks: for disk in physical_disks:
# calculate free disk space # calculate free disk space
free_space_mb[disk] = disk.free_size_mb free_space_mb[disk] = _get_disk_free_size_mb(disk, pending_delete)
disk_type = (disk.controller, disk.media_type, disk.interface_type, disk_type = (disk.controller, disk.media_type, disk.interface_type,
disk.size_mb) disk.size_mb)
@ -550,7 +570,8 @@ def _find_configuration(logical_disks, physical_disks):
if volumes_without_disks: if volumes_without_disks:
result, free_space_mb = ( result, free_space_mb = (
_assign_disks_to_volume(volumes_without_disks, _assign_disks_to_volume(volumes_without_disks,
physical_disks_by_type, free_space_mb)) physical_disks_by_type, free_space_mb,
pending_delete))
if not result: if not result:
# try again using the reserved physical disks in addition # try again using the reserved physical disks in addition
for disk_type, disks in physical_disks_by_type.items(): for disk_type, disks in physical_disks_by_type.items():
@ -560,7 +581,8 @@ def _find_configuration(logical_disks, physical_disks):
result, free_space_mb = ( result, free_space_mb = (
_assign_disks_to_volume(volumes_without_disks, _assign_disks_to_volume(volumes_without_disks,
physical_disks_by_type, physical_disks_by_type,
free_space_mb)) free_space_mb,
pending_delete))
if not result: if not result:
error_msg = _('failed to find matching physical disks for all ' error_msg = _('failed to find matching physical disks for all '
'logical disks') 'logical disks')
@ -636,7 +658,7 @@ def _calculate_volume_props(logical_disk, physical_disks, free_space_mb):
def _assign_disks_to_volume(logical_disks, physical_disks_by_type, def _assign_disks_to_volume(logical_disks, physical_disks_by_type,
free_space_mb): free_space_mb, pending_delete):
logical_disk = logical_disks.pop(0) logical_disk = logical_disks.pop(0)
raid_level = logical_disk['raid_level'] raid_level = logical_disk['raid_level']
@ -664,8 +686,12 @@ def _assign_disks_to_volume(logical_disks, physical_disks_by_type,
# filter out disks already in use if sharing is disabled # filter out disks already in use if sharing is disabled
if ('share_physical_disks' not in logical_disk if ('share_physical_disks' not in logical_disk
or not logical_disk['share_physical_disks']): or not logical_disk['share_physical_disks']):
initial_free_size_mb = {
disk: _get_disk_free_size_mb(disk, pending_delete)
for disk in disks
}
disks = [disk for disk in disks disks = [disk for disk in disks
if disk.free_size_mb == free_space_mb[disk]] if initial_free_size_mb[disk] == free_space_mb[disk]]
max_spans = _calculate_spans(raid_level, len(disks)) max_spans = _calculate_spans(raid_level, len(disks))
min_spans = min([2, max_spans]) min_spans = min([2, max_spans])
@ -701,7 +727,8 @@ def _assign_disks_to_volume(logical_disks, physical_disks_by_type,
result, candidate_free_space_mb = ( result, candidate_free_space_mb = (
_assign_disks_to_volume(logical_disks, _assign_disks_to_volume(logical_disks,
physical_disks_by_type, physical_disks_by_type,
candidate_free_space_mb)) candidate_free_space_mb,
pending_delete))
if result: if result:
logical_disks.append(candidate_volume) logical_disks.append(candidate_volume)
return (True, candidate_free_space_mb) return (True, candidate_free_space_mb)
@ -742,11 +769,12 @@ def _commit_to_controllers(node, controllers, substep="completed"):
enumerated value indicating whether the server must enumerated value indicating whether the server must
be rebooted only if raid controller does not support be rebooted only if raid controller does not support
realtime. realtime.
:param substep: contain sub cleaning step which executes any raid :param substep: contain sub cleaning or deploy step which executes any raid
configuration job if set after cleaning step. configuration job if set after cleaning or deploy step.
(default to completed) (default to completed)
:returns: states.CLEANWAIT if deletion is in progress asynchronously :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment) if
or None if it is completed. configuration is in progress asynchronously or None if it is
completed.
""" """
if not controllers: if not controllers:
LOG.debug('No changes on any of the controllers on node %s', LOG.debug('No changes on any of the controllers on node %s',
@ -796,9 +824,28 @@ def _commit_to_controllers(node, controllers, substep="completed"):
raid_controller) raid_controller)
node.driver_internal_info = driver_internal_info node.driver_internal_info = driver_internal_info
node.save()
return states.CLEANWAIT # Signal whether the node has been rebooted, that we do not need to execute
# the step again, and that this completion of this step is triggered
# through async polling.
# NOTE(mgoddard): set_async_step_flags calls node.save().
deploy_utils.set_async_step_flags(
node,
reboot=not all_realtime,
skip_current_step=True,
polling=True)
return deploy_utils.get_async_step_return_state(node)
def _get_disk_free_size_mb(disk, pending_delete):
"""Return the size of free space on the disk in MB.
:param disk: a PhysicalDisk object.
:param pending_delete: Whether there is a pending deletion of all virtual
disks.
"""
return disk.size_mb if pending_delete else disk.free_size_mb
class DracWSManRAID(base.RAIDInterface): class DracWSManRAID(base.RAIDInterface):
@ -807,6 +854,16 @@ class DracWSManRAID(base.RAIDInterface):
"""Return the properties of the interface.""" """Return the properties of the interface."""
return drac_common.COMMON_PROPERTIES return drac_common.COMMON_PROPERTIES
@base.deploy_step(priority=0,
argsinfo=base.RAID_APPLY_CONFIGURATION_ARGSINFO)
def apply_configuration(self, task, raid_config, create_root_volume=True,
create_nonroot_volumes=False,
delete_existing=True):
return super(DracRAID, self).apply_configuration(
task, raid_config, create_root_volume=create_root_volume,
create_nonroot_volumes=create_nonroot_volumes,
delete_existing=delete_existing)
@METRICS.timer('DracRAID.create_configuration') @METRICS.timer('DracRAID.create_configuration')
@base.clean_step(priority=0, abortable=False, argsinfo={ @base.clean_step(priority=0, abortable=False, argsinfo={
'create_root_volume': { 'create_root_volume': {
@ -822,11 +879,20 @@ class DracWSManRAID(base.RAIDInterface):
'Defaults to `True`.' 'Defaults to `True`.'
), ),
'required': False 'required': False
},
"delete_existing": {
"description": (
"Setting this to 'True' indicates to delete existing RAID "
"configuration prior to creating the new configuration. "
"Default value is 'False'."
),
"required": False,
} }
}) })
def create_configuration(self, task, def create_configuration(self, task,
create_root_volume=True, create_root_volume=True,
create_nonroot_volumes=True): create_nonroot_volumes=True,
delete_existing=False):
"""Create the RAID configuration. """Create the RAID configuration.
This method creates the RAID configuration on the given node. This method creates the RAID configuration on the given node.
@ -838,8 +904,12 @@ class DracWSManRAID(base.RAIDInterface):
:param create_nonroot_volumes: If True, non-root volumes are :param create_nonroot_volumes: If True, non-root volumes are
created. If False, no non-root volumes are created. Default created. If False, no non-root volumes are created. Default
is True. is True.
:returns: states.CLEANWAIT if creation is in progress asynchronously :param delete_existing: Setting this to True indicates to delete RAID
or None if it is completed. configuration prior to creating the new configuration. Default is
False.
:returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment)
if creation is in progress asynchronously or None if it is
completed.
:raises: MissingParameterValue, if node.target_raid_config is missing :raises: MissingParameterValue, if node.target_raid_config is missing
or empty. or empty.
:raises: DracOperationError on an error from python-dracclient. :raises: DracOperationError on an error from python-dracclient.
@ -864,13 +934,18 @@ class DracWSManRAID(base.RAIDInterface):
del disk['size_gb'] del disk['size_gb']
if delete_existing:
controllers = self._delete_configuration_no_commit(task)
else:
controllers = list()
physical_disks = list_physical_disks(node) physical_disks = list_physical_disks(node)
logical_disks = _find_configuration(logical_disks, physical_disks) logical_disks = _find_configuration(logical_disks, physical_disks,
pending_delete=delete_existing)
logical_disks_to_create = _filter_logical_disks( logical_disks_to_create = _filter_logical_disks(
logical_disks, create_root_volume, create_nonroot_volumes) logical_disks, create_root_volume, create_nonroot_volumes)
controllers = list()
for logical_disk in logical_disks_to_create: for logical_disk in logical_disks_to_create:
controller = dict() controller = dict()
controller_cap = create_virtual_disk( controller_cap = create_virtual_disk(
@ -892,28 +967,19 @@ class DracWSManRAID(base.RAIDInterface):
@METRICS.timer('DracRAID.delete_configuration') @METRICS.timer('DracRAID.delete_configuration')
@base.clean_step(priority=0) @base.clean_step(priority=0)
@base.deploy_step(priority=0)
def delete_configuration(self, task): def delete_configuration(self, task):
"""Delete the RAID configuration. """Delete the RAID configuration.
:param task: a TaskManager instance containing the node to act on. :param task: a TaskManager instance containing the node to act on.
:returns: states.CLEANWAIT if deletion is in progress asynchronously :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment)
or None if it is completed. if deletion is in progress asynchronously or None if it is
completed.
:raises: DracOperationError on an error from python-dracclient. :raises: DracOperationError on an error from python-dracclient.
""" """
node = task.node
controllers = list() controllers = self._delete_configuration_no_commit(task)
drac_raid_controllers = list_raid_controllers(node) return _commit_to_controllers(task.node, controllers,
for cntrl in drac_raid_controllers:
if _is_raid_controller(node, cntrl.id, drac_raid_controllers):
controller = dict()
controller_cap = _reset_raid_config(node, cntrl.id)
controller["raid_controller"] = cntrl.id
controller["is_reboot_required"] = controller_cap[
"is_reboot_required"]
controllers.append(controller)
return _commit_to_controllers(node, controllers,
substep="delete_foreign_config") substep="delete_foreign_config")
@METRICS.timer('DracRAID.get_logical_disks') @METRICS.timer('DracRAID.get_logical_disks')
@ -1005,18 +1071,18 @@ class DracWSManRAID(base.RAIDInterface):
if 'raid_config_substep' in node.driver_internal_info: if 'raid_config_substep' in node.driver_internal_info:
if node.driver_internal_info['raid_config_substep'] == \ if node.driver_internal_info['raid_config_substep'] == \
'delete_foreign_config': 'delete_foreign_config':
self._execute_cleaning_foreign_drives(task, node) self._execute_foreign_drives(task, node)
elif node.driver_internal_info['raid_config_substep'] == \ elif node.driver_internal_info['raid_config_substep'] == \
'completed': 'completed':
self._complete_raid_cleaning_substep(task, node) self._complete_raid_substep(task, node)
else: else:
self._complete_raid_cleaning_substep(task, node) self._complete_raid_substep(task, node)
else: else:
self._clear_raid_substep(node) self._clear_raid_substep(node)
self._clear_raid_config_job_failure(node) self._clear_raid_config_job_failure(node)
self._set_clean_failed(task, config_job) self._set_failed(task, config_job)
def _execute_cleaning_foreign_drives(self, task, node): def _execute_foreign_drives(self, task, node):
controllers = list() controllers = list()
jobs_required = False jobs_required = False
for controller_id in node.driver_internal_info[ for controller_id in node.driver_internal_info[
@ -1034,17 +1100,17 @@ class DracWSManRAID(base.RAIDInterface):
if not jobs_required: if not jobs_required:
LOG.info( LOG.info(
"No foreign drives detected, so " "No foreign drives detected, so "
"resume cleaning") "resume %s", "cleaning" if node.clean_step else "deployment")
self._complete_raid_cleaning_substep(task, node) self._complete_raid_substep(task, node)
else: else:
_commit_to_controllers( _commit_to_controllers(
node, node,
controllers, controllers,
substep='completed') substep='completed')
def _complete_raid_cleaning_substep(self, task, node): def _complete_raid_substep(self, task, node):
self._clear_raid_substep(node) self._clear_raid_substep(node)
self._resume_cleaning(task) self._resume(task)
def _clear_raid_substep(self, node): def _clear_raid_substep(self, node):
driver_internal_info = node.driver_internal_info driver_internal_info = node.driver_internal_info
@ -1076,7 +1142,7 @@ class DracWSManRAID(base.RAIDInterface):
node.driver_internal_info = driver_internal_info node.driver_internal_info = driver_internal_info
node.save() node.save()
def _set_clean_failed(self, task, config_job): def _set_failed(self, task, config_job):
LOG.error("RAID configuration job failed for node %(node)s. " LOG.error("RAID configuration job failed for node %(node)s. "
"Failed config job: %(config_job_id)s. " "Failed config job: %(config_job_id)s. "
"Message: '%(message)s'.", "Message: '%(message)s'.",
@ -1085,14 +1151,33 @@ class DracWSManRAID(base.RAIDInterface):
task.node.last_error = config_job.message task.node.last_error = config_job.message
task.process_event('fail') task.process_event('fail')
def _resume_cleaning(self, task): def _resume(self, task):
raid_common.update_raid_info( raid_common.update_raid_info(
task.node, self.get_logical_disks(task)) task.node, self.get_logical_disks(task))
driver_internal_info = task.node.driver_internal_info if task.node.clean_step:
driver_internal_info['cleaning_reboot'] = True manager_utils.notify_conductor_resume_clean(task)
task.node.driver_internal_info = driver_internal_info else:
task.node.save() manager_utils.notify_conductor_resume_deploy(task)
manager_utils.notify_conductor_resume_clean(task)
def _delete_configuration_no_commit(self, task):
"""Delete existing RAID configuration without committing the change.
:param task: A TaskManager instance.
:returns: A set of names of RAID controllers which need RAID changes to
be committed.
"""
node = task.node
controllers = list()
drac_raid_controllers = list_raid_controllers(node)
for cntrl in drac_raid_controllers:
if _is_raid_controller(node, cntrl.id, drac_raid_controllers):
controller = dict()
controller_cap = _reset_raid_config(node, cntrl.id)
controller["raid_controller"] = cntrl.id
controller["is_reboot_required"] = controller_cap[
"is_reboot_required"]
controllers.append(controller)
return controllers
class DracRAID(DracWSManRAID): class DracRAID(DracWSManRAID):

View File

@ -111,6 +111,25 @@ class DracJobTestCase(test_utils.BaseDracTest):
self.assertRaises(exception.DracOperationError, self.assertRaises(exception.DracOperationError,
drac_job.validate_job_queue, self.node) drac_job.validate_job_queue, self.node)
def test_validate_job_queue_name_prefix(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_jobs.return_value = [self.job]
drac_job.validate_job_queue(self.node, name_prefix='Fake')
mock_client.list_jobs.assert_called_once_with(only_unfinished=True)
def test_validate_job_queue_name_prefix_invalid(self,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_client.list_jobs.return_value = [self.job]
self.assertRaises(exception.DracOperationError,
drac_job.validate_job_queue, self.node,
name_prefix='ConfigBIOS')
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)

View File

@ -139,9 +139,8 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
autospec=True) autospec=True)
@mock.patch.object(drac_raid.DracRAID, 'get_logical_disks', @mock.patch.object(drac_raid.DracRAID, 'get_logical_disks',
spec_set=True, autospec=True) spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean') def _test__check_node_raid_jobs_with_completed_job(
def test__check_node_raid_jobs_with_completed_job( self, mock_notify_conductor_resume,
self, mock_notify_conductor_resume_clean,
mock_get_logical_disks, mock_get_drac_client): mock_get_logical_disks, mock_get_drac_client):
expected_logical_disk = {'size_gb': 558, expected_logical_disk = {'size_gb': 558,
'raid_level': '1', 'raid_level': '1',
@ -171,14 +170,29 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
self.node.driver_internal_info['raid_config_job_ids']) self.node.driver_internal_info['raid_config_job_ids'])
self.assertEqual([expected_logical_disk], self.assertEqual([expected_logical_disk],
self.node.raid_config['logical_disks']) self.node.raid_config['logical_disks'])
mock_notify_conductor_resume_clean.assert_called_once_with(task) mock_notify_conductor_resume.assert_called_once_with(task)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean')
def test__check_node_raid_jobs_with_completed_job_in_clean(
self, mock_notify_conductor_resume):
self.node.clean_step = {'foo': 'bar'}
self.node.save()
self._test__check_node_raid_jobs_with_completed_job(
mock_notify_conductor_resume)
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy')
def test__check_node_raid_jobs_with_completed_job_in_deploy(
self, mock_notify_conductor_resume):
self._test__check_node_raid_jobs_with_completed_job(
mock_notify_conductor_resume)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
def test__check_node_raid_jobs_with_failed_job(self, mock_get_drac_client): def test__check_node_raid_jobs_with_failed_job(self, mock_get_drac_client):
# mock node.driver_internal_info # mock node.driver_internal_info and node.clean_step
driver_internal_info = {'raid_config_job_ids': ['42']} driver_internal_info = {'raid_config_job_ids': ['42']}
self.node.driver_internal_info = driver_internal_info self.node.driver_internal_info = driver_internal_info
self.node.clean_step = {'foo': 'bar'}
self.node.save() self.node.save()
# mock task # mock task
task = mock.Mock(node=self.node, context=self.context) task = mock.Mock(node=self.node, context=self.context)
@ -207,9 +221,8 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
autospec=True) autospec=True)
@mock.patch.object(drac_raid.DracRAID, 'get_logical_disks', @mock.patch.object(drac_raid.DracRAID, 'get_logical_disks',
spec_set=True, autospec=True) spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean') def _test__check_node_raid_jobs_with_completed_job_already_failed(
def test__check_node_raid_jobs_with_completed_job_already_failed( self, mock_notify_conductor_resume,
self, mock_notify_conductor_resume_clean,
mock_get_logical_disks, mock_get_drac_client): mock_get_logical_disks, mock_get_drac_client):
expected_logical_disk = {'size_gb': 558, expected_logical_disk = {'size_gb': 558,
'raid_level': '1', 'raid_level': '1',
@ -242,14 +255,28 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
self.node.driver_internal_info) self.node.driver_internal_info)
self.assertNotIn('logical_disks', self.node.raid_config) self.assertNotIn('logical_disks', self.node.raid_config)
task.process_event.assert_called_once_with('fail') task.process_event.assert_called_once_with('fail')
self.assertFalse(mock_notify_conductor_resume.called)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean')
def test__check_node_raid_jobs_with_completed_job_already_failed_in_clean(
self, mock_notify_conductor_resume):
self.node.clean_step = {'foo': 'bar'}
self.node.save()
self._test__check_node_raid_jobs_with_completed_job_already_failed(
mock_notify_conductor_resume)
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy')
def test__check_node_raid_jobs_with_completed_job_already_failed_in_deploy(
self, mock_notify_conductor_resume):
self._test__check_node_raid_jobs_with_completed_job_already_failed(
mock_notify_conductor_resume)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid.DracRAID, 'get_logical_disks', @mock.patch.object(drac_raid.DracRAID, 'get_logical_disks',
spec_set=True, autospec=True) spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean') def _test__check_node_raid_jobs_with_multiple_jobs_completed(
def test__check_node_raid_jobs_with_multiple_jobs_completed( self, mock_notify_conductor_resume,
self, mock_notify_conductor_resume_clean,
mock_get_logical_disks, mock_get_drac_client): mock_get_logical_disks, mock_get_drac_client):
expected_logical_disk = {'size_gb': 558, expected_logical_disk = {'size_gb': 558,
'raid_level': '1', 'raid_level': '1',
@ -282,15 +309,28 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
self.node.driver_internal_info) self.node.driver_internal_info)
self.assertEqual([expected_logical_disk], self.assertEqual([expected_logical_disk],
self.node.raid_config['logical_disks']) self.node.raid_config['logical_disks'])
mock_notify_conductor_resume_clean.assert_called_once_with(task) mock_notify_conductor_resume.assert_called_once_with(task)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean')
def test__check_node_raid_jobs_with_multiple_jobs_completed_in_clean(
self, mock_notify_conductor_resume):
self.node.clean_step = {'foo': 'bar'}
self.node.save()
self._test__check_node_raid_jobs_with_multiple_jobs_completed(
mock_notify_conductor_resume)
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy')
def test__check_node_raid_jobs_with_multiple_jobs_completed_in_deploy(
self, mock_notify_conductor_resume):
self._test__check_node_raid_jobs_with_multiple_jobs_completed(
mock_notify_conductor_resume)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid.DracRAID, 'get_logical_disks', @mock.patch.object(drac_raid.DracRAID, 'get_logical_disks',
spec_set=True, autospec=True) spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean') def _test__check_node_raid_jobs_with_multiple_jobs_failed(
def test__check_node_raid_jobs_with_multiple_jobs_failed( self, mock_notify_conductor_resume,
self, mock_notify_conductor_resume_clean,
mock_get_logical_disks, mock_get_drac_client): mock_get_logical_disks, mock_get_drac_client):
expected_logical_disk = {'size_gb': 558, expected_logical_disk = {'size_gb': 558,
'raid_level': '1', 'raid_level': '1',
@ -327,3 +367,18 @@ class DracPeriodicTaskTestCase(db_base.DbTestCase):
self.node.driver_internal_info) self.node.driver_internal_info)
self.assertNotIn('logical_disks', self.node.raid_config) self.assertNotIn('logical_disks', self.node.raid_config)
task.process_event.assert_called_once_with('fail') task.process_event.assert_called_once_with('fail')
self.assertFalse(mock_notify_conductor_resume.called)
@mock.patch.object(manager_utils, 'notify_conductor_resume_clean')
def test__check_node_raid_jobs_with_multiple_jobs_failed_in_clean(
self, mock_notify_conductor_resume):
self.node.clean_step = {'foo': 'bar'}
self.node.save()
self._test__check_node_raid_jobs_with_multiple_jobs_failed(
mock_notify_conductor_resume)
@mock.patch.object(manager_utils, 'notify_conductor_resume_deploy')
def test__check_node_raid_jobs_with_multiple_jobs_failed_in_deploy(
self, mock_notify_conductor_resume):
self._test__check_node_raid_jobs_with_multiple_jobs_failed(
mock_notify_conductor_resume)

View File

@ -166,7 +166,8 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
drac_raid.create_virtual_disk( drac_raid.create_virtual_disk(
self.node, 'controller', ['disk1', 'disk2'], '1+0', 43008) self.node, 'controller', ['disk1', 'disk2'], '1+0', 43008)
mock_validate_job_queue.assert_called_once_with(self.node) mock_validate_job_queue.assert_called_once_with(
self.node, name_prefix='Config:RAID:controller')
mock_client.create_virtual_disk.assert_called_once_with( mock_client.create_virtual_disk.assert_called_once_with(
'controller', ['disk1', 'disk2'], '1+0', 43008, None, None, None) 'controller', ['disk1', 'disk2'], '1+0', 43008, None, None, None)
@ -182,7 +183,8 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
self.node, 'controller', ['disk1', 'disk2'], '1+0', 43008, self.node, 'controller', ['disk1', 'disk2'], '1+0', 43008,
disk_name='name', span_length=3, span_depth=2) disk_name='name', span_length=3, span_depth=2)
mock_validate_job_queue.assert_called_once_with(self.node) mock_validate_job_queue.assert_called_once_with(
self.node, name_prefix='Config:RAID:controller')
mock_client.create_virtual_disk.assert_called_once_with( mock_client.create_virtual_disk.assert_called_once_with(
'controller', ['disk1', 'disk2'], '1+0', 43008, 'name', 3, 2) 'controller', ['disk1', 'disk2'], '1+0', 43008, 'name', 3, 2)
@ -234,7 +236,8 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
drac_raid._reset_raid_config( drac_raid._reset_raid_config(
self.node, 'controller') self.node, 'controller')
mock_validate_job_queue.assert_called_once_with(self.node) mock_validate_job_queue.assert_called_once_with(
self.node, name_prefix='Config:RAID:controller')
mock_client.reset_raid_config.assert_called_once_with( mock_client.reset_raid_config.assert_called_once_with(
'controller') 'controller')
@ -262,7 +265,8 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
drac_raid.clear_foreign_config( drac_raid.clear_foreign_config(
self.node, 'RAID.Integrated.1-1') self.node, 'RAID.Integrated.1-1')
mock_validate_job_queue.assert_called_once_with(self.node) mock_validate_job_queue.assert_called_once_with(
self.node, 'Config:RAID:RAID.Integrated.1-1')
mock_client.clear_foreign_config.assert_called_once_with( mock_client.clear_foreign_config.assert_called_once_with(
'RAID.Integrated.1-1') 'RAID.Integrated.1-1')
@ -499,7 +503,8 @@ class DracCreateRaidConfigurationHelpersTestCase(test_utils.BaseDracTest):
'Disk.Bay.2:Enclosure.Internal.0-1:RAID.Integrated.1-1'] 'Disk.Bay.2:Enclosure.Internal.0-1:RAID.Integrated.1-1']
logical_disks = drac_raid._find_configuration(logical_disks, logical_disks = drac_raid._find_configuration(logical_disks,
physical_disks) physical_disks,
False)
self.assertEqual(expected_contoller, self.assertEqual(expected_contoller,
logical_disks[0]['controller']) logical_disks[0]['controller'])
@ -525,7 +530,8 @@ class DracCreateRaidConfigurationHelpersTestCase(test_utils.BaseDracTest):
'Disk.Bay.6:Enclosure.Internal.0-1:RAID.Integrated.1-1'] 'Disk.Bay.6:Enclosure.Internal.0-1:RAID.Integrated.1-1']
logical_disks = drac_raid._find_configuration(logical_disks, logical_disks = drac_raid._find_configuration(logical_disks,
physical_disks) physical_disks,
False)
self.assertEqual(expected_contoller, self.assertEqual(expected_contoller,
logical_disks[0]['controller']) logical_disks[0]['controller'])
@ -553,7 +559,8 @@ class DracCreateRaidConfigurationHelpersTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
logical_disks = drac_raid._find_configuration(logical_disks, logical_disks = drac_raid._find_configuration(logical_disks,
physical_disks) physical_disks,
False)
self.assertEqual(3, len(logical_disks)) self.assertEqual(3, len(logical_disks))
# step 1 # step 1
@ -591,6 +598,33 @@ class DracCreateRaidConfigurationHelpersTestCase(test_utils.BaseDracTest):
'Disk.Bay.3:Enclosure.Internal.0-1:RAID.Integrated.1-1']}, 'Disk.Bay.3:Enclosure.Internal.0-1:RAID.Integrated.1-1']},
logical_disks) logical_disks)
def test__find_configuration_pending_delete(self):
logical_disks = [
{'size_mb': 102400,
'raid_level': '5',
'is_root_volume': True,
'disk_type': 'hdd'}
]
physical_disks = self._generate_physical_disks()
# No free space, but deletion pending means they're still usable.
physical_disks = [disk._replace(free_size_mb=0)
for disk in physical_disks]
expected_contoller = 'RAID.Integrated.1-1'
expected_physical_disk_ids = [
'Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1',
'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1',
'Disk.Bay.2:Enclosure.Internal.0-1:RAID.Integrated.1-1']
logical_disks = drac_raid._find_configuration(logical_disks,
physical_disks,
True)
self.assertEqual(expected_contoller,
logical_disks[0]['controller'])
self.assertEqual(expected_physical_disk_ids,
logical_disks[0]['physical_disks'])
class DracRaidInterfaceTestCase(test_utils.BaseDracTest): class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@ -652,6 +686,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
[self.root_logical_disk] + self.nonroot_logical_disks) [self.root_logical_disk] + self.nonroot_logical_disks)
self.target_raid_configuration = {'logical_disks': self.logical_disks} self.target_raid_configuration = {'logical_disks': self.logical_disks}
self.node.target_raid_config = self.target_raid_configuration self.node.target_raid_config = self.target_raid_configuration
self.node.clean_step = {'foo': 'bar'}
self.node.save() self.node.save()
def _generate_physical_disks(self): def _generate_physical_disks(self):
@ -664,26 +699,43 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True, @mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True) autospec=True)
def test_create_configuration( def _test_create_configuration(
self, mock_commit_config, mock_validate_job_queue, self, expected_state, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client): mock_list_physical_disks, mock__reset_raid_config,
mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42' mock_commit_config.return_value = '42'
raid_controller_dict = {
'id': 'RAID.Integrated.1-1',
'description': 'Integrated RAID Controller 1',
'manufacturer': 'DELL',
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1',
'supports_realtime': True}
raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
mock_client.list_raid_controllers.return_value = [raid_controller]
mock__reset_raid_config.return_value = {
'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True}
mock_client.create_virtual_disk.return_value = { mock_client.create_virtual_disk.return_value = {
'is_reboot_required': constants.RebootRequired.optional, 'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True} 'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( return_value = task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=False) task, create_root_volume=True, create_nonroot_volumes=False)
mock_client.create_virtual_disk.assert_called_once_with( mock_client.create_virtual_disk.assert_called_once_with(
@ -696,10 +748,19 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
task.node, raid_controller='RAID.Integrated.1-1', reboot=False, task.node, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True) realtime=True)
self.assertEqual(expected_state, return_value)
self.node.refresh() self.node.refresh()
self.assertEqual(['42'], self.assertEqual(['42'],
self.node.driver_internal_info['raid_config_job_ids']) self.node.driver_internal_info['raid_config_job_ids'])
def test_create_configuration_in_clean(self):
self._test_create_configuration(states.CLEANWAIT)
def test_create_configuration_in_deploy(self):
self.node.clean_step = None
self.node.save()
self._test_create_configuration(states.DEPLOYWAIT)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@ -709,7 +770,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True) autospec=True)
def test_create_configuration_no_change( def test_create_configuration_no_change(
self, mock_commit_config, mock_validate_job_queue, self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client): mock_list_physical_disks,
mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
@ -721,7 +783,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
return_value = task.driver.raid.create_configuration( return_value = task.driver.raid.create_configuration(
task, create_root_volume=False, create_nonroot_volumes=False) task, create_root_volume=False, create_nonroot_volumes=False,
delete_existing=False)
self.assertEqual(0, mock_client.create_virtual_disk.call_count) self.assertEqual(0, mock_client.create_virtual_disk.call_count)
self.assertEqual(0, mock_commit_config.call_count) self.assertEqual(0, mock_commit_config.call_count)
@ -731,6 +794,72 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
self.node.refresh() self.node.refresh()
self.assertNotIn('raid_config_job_ids', self.node.driver_internal_info) self.assertNotIn('raid_config_job_ids', self.node.driver_internal_info)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', autospec=True)
@mock.patch.object(drac_raid, 'list_virtual_disks', autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
def test_create_configuration_delete_existing(
self, mock_commit_config,
mock_validate_job_queue,
mock_list_physical_disks,
mock_list_virtual_disks,
mock__reset_raid_config,
mock_get_drac_client):
self.node.clean_step = None
self.node.save()
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
physical_disks = self._generate_physical_disks()
raid_controller_dict = {
'id': 'RAID.Integrated.1-1',
'description': 'Integrated RAID Controller 1',
'manufacturer': 'DELL',
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1',
'supports_realtime': True}
raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.list_raid_controllers.return_value = [raid_controller]
mock__reset_raid_config.return_value = {
'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True}
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
return_value = task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=False,
delete_existing=True)
mock_client.create_virtual_disk.assert_called_once_with(
'RAID.Integrated.1-1',
['Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1',
'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'],
'1', 51200, None, 2, 1)
mock_commit_config.assert_called_once_with(
task.node, raid_controller='RAID.Integrated.1-1',
realtime=True, reboot=False)
self.assertEqual(1, mock_commit_config.call_count)
self.assertEqual(states.DEPLOYWAIT, return_value)
self.node.refresh()
self.assertEqual(['42'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@ -765,7 +894,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_called_once_with( mock_client.create_virtual_disk.assert_called_once_with(
'RAID.Integrated.1-1', 'RAID.Integrated.1-1',
@ -821,7 +951,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_called_once_with( mock_client.create_virtual_disk.assert_called_once_with(
'RAID.Integrated.1-1', 'RAID.Integrated.1-1',
@ -873,7 +1004,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
'controller-2', 'controller-2',
@ -941,7 +1073,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
'RAID.Integrated.1-1', 'RAID.Integrated.1-1',
@ -1004,7 +1137,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
@ -1069,7 +1203,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
@ -1162,7 +1297,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
@ -1189,6 +1325,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True) autospec=True)
@ -1196,7 +1333,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True) autospec=True)
def test_create_configuration_fails_with_sharing_disabled( def test_create_configuration_fails_with_sharing_disabled(
self, mock_commit_config, mock_validate_job_queue, self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client): mock_list_physical_disks, mock__reset_raid_config,
mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
@ -1210,7 +1348,18 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
self.physical_disks = self.physical_disks[0:3] self.physical_disks = self.physical_disks[0:3]
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks mock_list_physical_disks.return_value = physical_disks
raid_controller_dict = {
'id': 'RAID.Integrated.1-1',
'description': 'Integrated RAID Controller 1',
'manufacturer': 'DELL',
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1',
'supports_realtime': True}
raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
mock_client.list_raid_controllers.return_value = [raid_controller]
mock_commit_config.return_value = '42' mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = { mock_client.create_virtual_disk.return_value = {
'is_reboot_required': constants.RebootRequired.optional, 'is_reboot_required': constants.RebootRequired.optional,
@ -1262,7 +1411,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.raid.create_configuration( task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
mock_client.create_virtual_disk.assert_has_calls( mock_client.create_virtual_disk.assert_has_calls(
[mock.call( [mock.call(
@ -1332,10 +1482,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
self.assertRaises( self.assertRaises(
exception.DracOperationError, exception.DracOperationError,
task.driver.raid.create_configuration, task.driver.raid.create_configuration,
task, create_root_volume=True, create_nonroot_volumes=True) task, create_root_volume=True, create_nonroot_volumes=True,
delete_existing=False)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True) autospec=True)
@ -1346,7 +1498,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
def test_create_configuration_fails_if_not_enough_space( def test_create_configuration_fails_if_not_enough_space(
self, mock_create_virtual_disk, mock_commit_config, self, mock_create_virtual_disk, mock_commit_config,
mock_validate_job_queue, mock_list_physical_disks, mock_validate_job_queue, mock_list_physical_disks,
mock_get_drac_client): mock__reset_raid_config, mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
@ -1362,6 +1514,21 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
self.physical_disks = self.physical_disks[0:3] self.physical_disks = self.physical_disks[0:3]
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks mock_list_physical_disks.return_value = physical_disks
raid_controller_dict = {
'id': 'RAID.Integrated.1-1',
'description': 'Integrated RAID Controller 1',
'manufacturer': 'DELL',
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1',
'supports_realtime': True}
raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
mock_client.list_raid_controllers.return_value = [raid_controller]
mock__reset_raid_config.return_value = {
'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True}
mock_commit_config.return_value = '42' mock_commit_config.return_value = '42'
mock_create_virtual_disk.return_value = { mock_create_virtual_disk.return_value = {
@ -1377,6 +1544,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', spec_set=True,
autospec=True)
@mock.patch.object(drac_raid, 'list_physical_disks', autospec=True) @mock.patch.object(drac_raid, 'list_physical_disks', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True) autospec=True)
@ -1387,7 +1556,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
def test_create_configuration_fails_if_disk_already_reserved( def test_create_configuration_fails_if_disk_already_reserved(
self, mock_create_virtual_disk, mock_commit_config, self, mock_create_virtual_disk, mock_commit_config,
mock_validate_job_queue, mock_list_physical_disks, mock_validate_job_queue, mock_list_physical_disks,
mock_get_drac_client): mock__reset_raid_config, mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
@ -1406,7 +1575,24 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks() physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks mock_list_physical_disks.return_value = physical_disks
raid_controller_dict = {
'id': 'RAID.Integrated.1-1',
'description': 'Integrated RAID Controller 1',
'manufacturer': 'DELL',
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1',
'supports_realtime': True}
raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
mock_client.list_raid_controllers.return_value = [raid_controller]
mock_commit_config.return_value = '42' mock_commit_config.return_value = '42'
mock__reset_raid_config.return_value = {
'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True}
mock_create_virtual_disk.return_value = { mock_create_virtual_disk.return_value = {
'is_reboot_required': constants.RebootRequired.optional, 'is_reboot_required': constants.RebootRequired.optional,
'is_commit_required': True} 'is_commit_required': True}
@ -1420,18 +1606,18 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', autospec=True)
@mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True) @mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True)
@mock.patch.object(drac_job, 'validate_job_queue', spec_set=True, @mock.patch.object(drac_job, 'validate_job_queue', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True, @mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, '_reset_raid_config', spec_set=True, def _test_delete_configuration(self, expected_state,
autospec=True) mock_commit_config,
def test_delete_configuration(self, mock__reset_raid_config, mock_validate_job_queue,
mock_commit_config, mock_list_raid_controllers,
mock_validate_job_queue, mock__reset_raid_config,
mock_list_raid_controllers, mock_get_drac_client):
mock_get_drac_client):
mock_client = mock.Mock() mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client mock_get_drac_client.return_value = mock_client
raid_controller_dict = { raid_controller_dict = {
@ -1459,11 +1645,19 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
task.node, raid_controller='RAID.Integrated.1-1', reboot=False, task.node, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True) realtime=True)
self.assertEqual(states.CLEANWAIT, return_value) self.assertEqual(expected_state, return_value)
self.node.refresh() self.node.refresh()
self.assertEqual(['42'], self.assertEqual(['42'],
self.node.driver_internal_info['raid_config_job_ids']) self.node.driver_internal_info['raid_config_job_ids'])
def test_delete_configuration_in_clean(self):
self._test_delete_configuration(states.CLEANWAIT)
def test_delete_configuration_in_deploy(self):
self.node.clean_step = None
self.node.save()
self._test_delete_configuration(states.DEPLOYWAIT)
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True, @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
autospec=True) autospec=True)
@mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True) @mock.patch.object(drac_raid, 'list_raid_controllers', autospec=True)
@ -1568,7 +1762,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
return_value = task.driver.raid._execute_cleaning_foreign_drives( return_value = task.driver.raid._execute_foreign_drives(
task, self.node) task, self.node)
mock_resume.assert_called_once_with( mock_resume.assert_called_once_with(
task, 'cleaning', 'continue_node_clean') task, 'cleaning', 'continue_node_clean')

View File

@ -0,0 +1,10 @@
---
features:
- |
Adds support for deploy steps to ``raid`` interface of ``idrac``
hardware type. The methods ``apply_configuration`` and
``delete_configuration`` can be used as deploy steps.
- |
Adds a new ``delete_existing`` argument to the ``create_configuration``
clean step on the ``idrac`` ``raid`` interface which can be used to
delete existing virtual disks. The default for this argument is ``False``.