Browse Source

Add iDRAC driver realtime RAID creation and deletion

It adds capability to the idrac driver for creating and deleting
RAID sets without rebooting the bare metal node.


Change-Id: I3f4282afff860d9771a3d37f4d5e1172d88e87a3
Story: 2004861
changes/03/634903/25
digambar 3 years ago committed by Digambar
parent
commit
3a54fe3e85
  1. 2
      driver-requirements.txt
  2. 57
      ironic/drivers/modules/drac/raid.py
  3. 206
      ironic/tests/unit/drivers/modules/drac/test_raid.py
  4. 12
      releasenotes/notes/add-realtime-support-d814d5917836e9e2.yaml

2
driver-requirements.txt

@ -9,7 +9,7 @@ pysnmp>=4.3.0,<5.0.0
python-ironic-inspector-client>=1.5.0
python-scciclient>=0.8.0
UcsSdk==0.8.2.2;python_version<'3'
python-dracclient>=1.5.0,<3.0.0
python-dracclient>=3.0.0,<4.0.0
python-xclarityclient>=0.1.6
# The CIMC drivers use the Cisco IMC SDK version 0.7.2 or greater

57
ironic/drivers/modules/drac/raid.py

@ -202,20 +202,25 @@ def delete_virtual_disk(node, virtual_disk):
raise exception.DracOperationError(error=exc)
def commit_config(node, raid_controller, reboot=False):
def commit_config(node, raid_controller, reboot=False, realtime=False):
"""Apply all pending changes on a RAID controller.
:param node: an ironic node object.
:param raid_controller: id of the RAID controller.
:param reboot: indicates whether a reboot job should be automatically
created with the config job. (optional, defaults to False)
:param realtime: indicates RAID controller supports realtime.
(optional, defaults to False)
:returns: id of the created job
:raises: DracOperationError on an error from python-dracclient.
"""
client = drac_common.get_drac_client(node)
try:
return client.commit_pending_raid_changes(raid_controller, reboot)
return client.commit_pending_raid_changes(
raid_controller=raid_controller,
reboot=reboot,
realtime=realtime)
except drac_exceptions.BaseClientException as exc:
LOG.error('DRAC driver failed to commit pending RAID config for'
' controller %(raid_controller_fqdd)s on node '
@ -642,15 +647,25 @@ def _commit_to_controllers(node, controllers):
if 'raid_config_job_ids' not in driver_internal_info:
driver_internal_info['raid_config_job_ids'] = []
controllers = list(controllers)
all_realtime = True
for controller in controllers:
# Do a reboot only for the last controller
raid_controller = controller['raid_controller']
# Commit the configuration
# The logic below will reboot the node if there is at least one
# controller without real time support. In that case the reboot
# is triggered when the configuration is committed to the last
# controller.
realtime = controller['is_reboot_required'] == 'optional'
all_realtime = all_realtime and realtime
if controller == controllers[-1]:
job_id = commit_config(node, raid_controller=controller,
reboot=True)
job_id = commit_config(node, raid_controller=raid_controller,
reboot=not all_realtime,
realtime=realtime)
else:
job_id = commit_config(node, raid_controller=controller,
reboot=False)
job_id = commit_config(node, raid_controller=raid_controller,
reboot=False,
realtime=realtime)
LOG.info('Change has been committed to RAID controller '
'%(controller)s on node %(node)s. '
@ -735,10 +750,10 @@ class DracRAID(base.RAIDInterface):
logical_disks_to_create = _filter_logical_disks(
logical_disks, create_root_volume, create_nonroot_volumes)
controllers = set()
controllers = list()
for logical_disk in logical_disks_to_create:
controllers.add(logical_disk['controller'])
create_virtual_disk(
controller = dict()
controller_cap = create_virtual_disk(
node,
raid_controller=logical_disk['controller'],
physical_disks=logical_disk['physical_disks'],
@ -747,8 +762,12 @@ class DracRAID(base.RAIDInterface):
disk_name=logical_disk.get('name'),
span_length=logical_disk.get('span_length'),
span_depth=logical_disk.get('span_depth'))
controller['raid_controller'] = logical_disk['controller']
controller['is_reboot_required'] = controller_cap[
'is_reboot_required']
controllers.append(controller)
return _commit_to_controllers(node, list(controllers))
return _commit_to_controllers(node, controllers)
@METRICS.timer('DracRAID.delete_configuration')
@base.clean_step(priority=0)
@ -762,12 +781,16 @@ class DracRAID(base.RAIDInterface):
"""
node = task.node
controllers = set()
controllers = list()
for disk in list_virtual_disks(node):
controllers.add(disk.controller)
delete_virtual_disk(node, disk.id)
return _commit_to_controllers(node, list(controllers))
controller = dict()
controller_cap = delete_virtual_disk(node, disk.id)
controller['raid_controller'] = disk.controller
controller['is_reboot_required'] = controller_cap[
'is_reboot_required']
controllers.append(controller)
return _commit_to_controllers(node, controllers)
@METRICS.timer('DracRAID.get_logical_disks')
def get_logical_disks(self, task):

206
ironic/tests/unit/drivers/modules/drac/test_raid.py

@ -47,7 +47,8 @@ class DracQueryRaidConfigurationTestCase(test_utils.BaseDracTest):
'model': 'PERC H710 Mini',
'primary_status': 'ok',
'firmware_version': '21.3.0-0009',
'bus': '1'}
'bus': '1',
'supports_realtime': True}
self.raid_controller = test_utils.make_raid_controller(
raid_controller_dict)
@ -228,16 +229,27 @@ class DracManageVirtualDisksTestCase(test_utils.BaseDracTest):
drac_raid.commit_config(self.node, 'controller1')
mock_client.commit_pending_raid_changes.assert_called_once_with(
'controller1', False)
raid_controller='controller1', reboot=False, realtime=False)
def test_commit_config_with_reboot(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
drac_raid.commit_config(self.node, 'controller1', reboot=True)
drac_raid.commit_config(self.node, 'controller1', reboot=True,
realtime=False)
mock_client.commit_pending_raid_changes.assert_called_once_with(
'controller1', True)
raid_controller='controller1', reboot=True, realtime=False)
def test_commit_config_with_realtime(self, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
drac_raid.commit_config(self.node, 'RAID.Integrated.1-1', reboot=False,
realtime=True)
mock_client.commit_pending_raid_changes.assert_called_once_with(
raid_controller='RAID.Integrated.1-1', reboot=False, realtime=True)
def test_commit_config_fail(self, mock_get_drac_client):
mock_client = mock.Mock()
@ -607,6 +619,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -618,8 +634,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
['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', reboot=True)
task.node, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
@ -639,6 +657,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_get_drac_client.return_value = mock_client
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -671,6 +693,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
'raid_level': '5+0',
'is_root_volume': True
}
self.logical_disks = [self.root_logical_disk]
self.target_raid_configuration = {'logical_disks': self.logical_disks}
self.node.target_raid_config = self.target_raid_configuration
@ -680,6 +703,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -698,7 +725,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
@ -712,8 +740,9 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
def test_create_configuration_with_nested_raid_10(
self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client):
self, mock_commit_config,
mock_validate_job_queue, mock_list_physical_disks,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
@ -731,6 +760,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -747,7 +780,8 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
@ -771,13 +805,26 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.side_effect = ['42', '12']
mock_commit_config.side_effect = ['42', '12', '13']
mock_client.create_virtual_disk.side_effect = [{
'is_reboot_required': 'True',
'commit_required': True,
'is_commit_required': True
}, {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}, {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}]
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True)
mock_client.create_virtual_disk.assert_has_calls(
[mock.call(
'controller-2',
@ -798,23 +845,17 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
'5', 102400, None, 3, 1)
],
any_order=True)
# Commits to both controller
mock_commit_config.assert_has_calls(
[mock.call(mock.ANY, raid_controller='controller-2',
reboot=mock.ANY),
reboot=mock.ANY, realtime=mock.ANY),
mock.call(mock.ANY, raid_controller='RAID.Integrated.1-1',
reboot=mock.ANY)],
any_order=True)
# One of the config jobs should issue a reboot
mock_commit_config.assert_has_calls(
[mock.call(mock.ANY, raid_controller=mock.ANY,
reboot=False),
mock.call(mock.ANY, raid_controller=mock.ANY,
reboot=True)],
reboot=mock.ANY, realtime=mock.ANY)],
any_order=True)
self.node.refresh()
self.assertEqual(['42', '12'],
self.assertEqual(['42', '12', '13'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -842,13 +883,17 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_commit_config.side_effect = ['42', '12', '13']
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.raid.create_configuration(
task, create_root_volume=True, create_nonroot_volumes=True)
mock_client.create_virtual_disk.assert_has_calls(
[mock.call(
'RAID.Integrated.1-1',
@ -870,11 +915,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
any_order=True)
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock_commit_config.assert_called_with(
mock.ANY, raid_controller='RAID.Integrated.1-1',
reboot=False, realtime=True)
self.node.refresh()
self.assertEqual(['42'],
self.assertEqual(['42', '12', '13'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -887,6 +933,7 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
def test_create_configuration_with_predefined_number_of_phyisical_disks(
self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
@ -900,8 +947,13 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.side_effect = ['42', '12']
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -924,11 +976,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
any_order=True)
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock_commit_config.assert_called_with(
mock.ANY, raid_controller='RAID.Integrated.1-1',
reboot=False, realtime=True)
self.node.refresh()
self.assertEqual(['42'],
self.assertEqual(['42', '12'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -961,7 +1014,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_commit_config.side_effect = ['42', '12', '13']
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -989,11 +1047,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
any_order=True)
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock_commit_config.assert_called_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
self.assertEqual(['42', '12', '13'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -1049,7 +1108,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
physical_disks = self._generate_physical_disks()
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_commit_config.side_effect = ['42', '12']
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1071,11 +1135,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
'5', 102400, None, 3, 1)])
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock_commit_config.assert_called_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
self.assertEqual(['42', '12'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -1103,6 +1168,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1141,6 +1210,11 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True
}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1163,11 +1237,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
any_order=True)
# Commits to the controller
mock_commit_config.assert_called_once_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=True)
mock_commit_config.assert_called_with(
mock.ANY, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.node.refresh()
self.assertEqual(['42'],
self.assertEqual(['42', '42'],
self.node.driver_internal_info['raid_config_job_ids'])
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@ -1205,6 +1280,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_client.create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1220,9 +1299,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
@mock.patch.object(drac_raid, 'create_virtual_disk', spec_set=True,
autospec=True)
def test_create_configuration_fails_if_not_enough_space(
self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client):
self, mock_create_virtual_disk, mock_commit_config,
mock_validate_job_queue, mock_list_physical_disks,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
@ -1240,6 +1322,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1255,9 +1341,12 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
@mock.patch.object(drac_raid, 'create_virtual_disk', spec_set=True,
autospec=True)
def test_create_configuration_fails_if_disk_already_reserved(
self, mock_commit_config, mock_validate_job_queue,
mock_list_physical_disks, mock_get_drac_client):
self, mock_create_virtual_disk, mock_commit_config,
mock_validate_job_queue, mock_list_physical_disks,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
@ -1277,6 +1366,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_physical_disks.return_value = physical_disks
mock_commit_config.return_value = '42'
mock_create_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
@ -1292,7 +1385,10 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
def test_delete_configuration(self, mock_commit_config,
@mock.patch.object(drac_raid, 'delete_virtual_disk', spec_set=True,
autospec=True)
def test_delete_configuration(self, mock_delete_virtual_disk,
mock_commit_config,
mock_validate_job_queue,
mock_list_virtual_disks,
mock_get_drac_client):
@ -1314,15 +1410,18 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
mock_list_virtual_disks.return_value = [
test_utils.make_virtual_disk(virtual_disk_dict)]
mock_commit_config.return_value = '42'
mock_delete_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
return_value = task.driver.raid.delete_configuration(task)
mock_client.delete_virtual_disk.assert_called_once_with(
'Disk.Virtual.0:RAID.Integrated.1-1')
mock_commit_config.assert_called_once_with(
task.node, raid_controller='RAID.Integrated.1-1', reboot=True)
task.node, raid_controller='RAID.Integrated.1-1', reboot=False,
realtime=True)
self.assertEqual(states.CLEANWAIT, return_value)
self.node.refresh()
@ -1336,13 +1435,20 @@ class DracRaidInterfaceTestCase(test_utils.BaseDracTest):
autospec=True)
@mock.patch.object(drac_raid, 'commit_config', spec_set=True,
autospec=True)
def test_delete_configuration_no_change(self, mock_commit_config,
@mock.patch.object(drac_raid, 'delete_virtual_disk', spec_set=True,
autospec=True)
def test_delete_configuration_no_change(self, mock_delete_virtual_disk,
mock_commit_config,
mock_validate_job_queue,
mock_list_virtual_disks,
mock_get_drac_client):
mock_client = mock.Mock()
mock_get_drac_client.return_value = mock_client
mock_list_virtual_disks.return_value = []
mock_delete_virtual_disk.return_value = {
'is_reboot_required': 'optional',
'commit_required': False,
'is_commit_required': True}
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:

12
releasenotes/notes/add-realtime-support-d814d5917836e9e2.yaml

@ -0,0 +1,12 @@
---
features:
- |
Adds capability to hardware type ``idrac`` for creating and
deleting RAID sets without rebooting the baremetal node.
This realtime mechanism is supported on PERC H730 and H740
RAID controllers that are running firmware version 25.5.5.0005
or later.
upgrade:
- |
This change requires python-dracclient version 3.0.0 and later.
Loading…
Cancel
Save