Support to perform erase on all the disks(volumes and drives)
This commit adds change to perform generic IPA `erase_devices` on success of Sanitize disk erase. Also, performs disk erase by overwriting the disks with zero when Sanitize disk erase is not supported. Change-Id: I40e502a24dd2ae5f0ce4e24c4b239ce6b743f0af Closes-Bug: #1666518
This commit is contained in:
parent
7baf09af8d
commit
69f21a99aa
@ -371,18 +371,11 @@ def erase_devices():
|
||||
"""
|
||||
server = objects.Server()
|
||||
|
||||
select_controllers = lambda x: (x.properties.get(
|
||||
'Sanitize Erase Supported',
|
||||
False) == 'True')
|
||||
_select_controllers_by(server, select_controllers,
|
||||
'Sanitize Erase Supported')
|
||||
|
||||
for controller in server.controllers:
|
||||
drives = [x for x in controller.unassigned_physical_drives
|
||||
if (x.get_physical_drive_dict().get('erase_status', '')
|
||||
== 'OK')]
|
||||
if drives:
|
||||
drives = ','.join(x.id for x in drives)
|
||||
controller.erase_devices(drives)
|
||||
|
||||
common.wait_for_operation_to_complete(
|
||||
@ -397,6 +390,16 @@ def erase_devices():
|
||||
for controller in server.controllers:
|
||||
drive_status = {x.id: x.erase_status
|
||||
for x in controller.unassigned_physical_drives}
|
||||
sanitize_supported = controller.properties.get(
|
||||
'Sanitize Erase Supported', 'False')
|
||||
if sanitize_supported == 'False':
|
||||
msg = ("Drives overwritten with zeros because sanitize erase "
|
||||
"is not supported on the controller.")
|
||||
else:
|
||||
msg = ("Sanitize Erase performed on the disks attached to "
|
||||
"the controller.")
|
||||
|
||||
drive_status.update({'Summary': msg})
|
||||
status[controller.id] = drive_status
|
||||
|
||||
return status
|
||||
|
@ -418,14 +418,52 @@ class Controller(object):
|
||||
"""
|
||||
self.execute_cmd("logicaldrive", "all", "delete", "forced")
|
||||
|
||||
def erase_devices(self, drive):
|
||||
def _get_erase_command(self, drive, pattern):
|
||||
"""Return the command arguments based on the pattern.
|
||||
|
||||
Erase command examples:
|
||||
1) Sanitize: "ssacli ctrl slot=0 pd 1I:1:1 modify erase
|
||||
erasepattern=overwrite unrestricted=off forced"
|
||||
2) Zeros: "ssacli ctrl slot=0 pd 1I:1:1 modify erase
|
||||
erasepattern=zero forced"
|
||||
|
||||
:param drive: A string with comma separated list of drives.
|
||||
:param pattern: A string which defines the type of erase.
|
||||
:returns: A list of ssacli command arguments.
|
||||
"""
|
||||
cmd_args = []
|
||||
cmd_args.append("pd %s" % drive)
|
||||
cmd_args.extend(['modify', 'erase',
|
||||
'erasepattern=overwrite',
|
||||
'unrestricted=off',
|
||||
'forced'])
|
||||
self.execute_cmd(*cmd_args)
|
||||
cmd_args.extend(['modify', 'erase', pattern])
|
||||
|
||||
if pattern != 'erasepattern=zero':
|
||||
cmd_args.append('unrestricted=off')
|
||||
|
||||
cmd_args.append('forced')
|
||||
return cmd_args
|
||||
|
||||
def erase_devices(self, drives):
|
||||
"""Perform Erase on all the drives in the controller.
|
||||
|
||||
This method erases all the drives in the controller by overwriting
|
||||
the drives with the pattern. The drives will be unavailable until
|
||||
after successful completion or failure. The possible erase pattern
|
||||
available for sanitize erase are 'overwrite' and 'block' to perform
|
||||
erase on HDD and SSD respectively.
|
||||
|
||||
If the sanitize erase is not supported on the controller it performs
|
||||
the disk erase by overwritting with zeros.
|
||||
|
||||
:param drives: A list of drive objects in the controller.
|
||||
"""
|
||||
# TODO(aparnav): Add Sanitize erase support for SSD
|
||||
drive = ','.join([x.id for x in drives])
|
||||
cmd_args = self._get_erase_command(drive, 'erasepattern=overwrite')
|
||||
|
||||
stdout = self.execute_cmd(*cmd_args)
|
||||
if "not supported" in str(stdout):
|
||||
cmd_args = self._get_erase_command(drive,
|
||||
'erasepattern=zero')
|
||||
self.execute_cmd(*cmd_args)
|
||||
|
||||
|
||||
class RaidArray(object):
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
from ironic_python_agent import hardware
|
||||
|
||||
from proliantutils import exception
|
||||
from proliantutils.hpssa import manager as hpssa_manager
|
||||
from proliantutils.hpsum import hpsum_controller
|
||||
|
||||
@ -92,19 +91,22 @@ class ProliantHardwareManager(hardware.GenericHardwareManager):
|
||||
def erase_devices(self, node, port):
|
||||
"""Erase the drives on the bare metal.
|
||||
|
||||
This method erase all the drives which supports sanitize on
|
||||
bare metal. If fails, it falls back to the generic erase method.
|
||||
This method erase all the drives which supports sanitize and the drives
|
||||
which are not part of any logical volume on the bare metal. It calls
|
||||
generic erase method after the success of Sanitize disk erase.
|
||||
:param node: A dictionary of the node object.
|
||||
:param port: A list of dictionaries containing information of ports
|
||||
for the node.
|
||||
:raises exception.HPSSAOperationError, if there is a failure on the
|
||||
erase operation on the controllers.
|
||||
:returns: The dictionary of controllers with the drives and erase
|
||||
status for each drive.
|
||||
"""
|
||||
try:
|
||||
result = {}
|
||||
result['Sanitize Erase'] = hpssa_manager.erase_devices()
|
||||
|
||||
except exception.HPSSAOperationError:
|
||||
result.update(super(ProliantHardwareManager,
|
||||
self).erase_devices(node, port))
|
||||
result = {}
|
||||
result['Disk Erase Status'] = hpssa_manager.erase_devices()
|
||||
|
||||
result.update(super(ProliantHardwareManager,
|
||||
self).erase_devices(node, port))
|
||||
return result
|
||||
|
||||
def update_firmware(self, node, port):
|
||||
|
@ -2360,33 +2360,9 @@ Smart Array P440 in Slot 2
|
||||
Port Location: Internal
|
||||
Managed Cable Connected: False
|
||||
|
||||
|
||||
Internal Drive Cage at Port 1I, Box 0, OK
|
||||
|
||||
Power Supply Status: Not Redundant
|
||||
Drive Bays: 4
|
||||
Port: 1I
|
||||
Box: 0
|
||||
Location: Internal
|
||||
|
||||
Physical Drives
|
||||
None attached
|
||||
|
||||
|
||||
|
||||
Internal Drive Cage at Port 1I, Box 2, OK
|
||||
|
||||
Power Supply Status: Not Redundant
|
||||
Drive Bays: 4
|
||||
Port: 1I
|
||||
Box: 2
|
||||
Location: Internal
|
||||
|
||||
Physical Drives
|
||||
physicaldrive 1I:2:1 (port 1I:box 2:bay 1, SAS HDD, 300 GB, OK)
|
||||
|
||||
|
||||
|
||||
unassigned
|
||||
|
||||
physicaldrive 1I:2:1
|
||||
@ -2425,11 +2401,6 @@ Smart Array P440 in Slot 2
|
||||
Primary Boot Volume: None
|
||||
Secondary Boot Volume: None
|
||||
|
||||
Physical Drives
|
||||
physicaldrive 1I:2:1 (port 1I:box 2:bay 1, SAS HDD, 300 GB, OK)
|
||||
|
||||
|
||||
|
||||
unassigned
|
||||
|
||||
physicaldrive 1I:2:1
|
||||
@ -2451,11 +2422,6 @@ Smart Array P440 in Slot 2
|
||||
Primary Boot Volume: None
|
||||
Secondary Boot Volume: None
|
||||
|
||||
Physical Drives
|
||||
physicaldrive 1I:2:1 (port 1I:box 2:bay 1, SAS HDD, 300 GB, OK)
|
||||
|
||||
|
||||
|
||||
unassigned
|
||||
|
||||
physicaldrive 1I:2:1
|
||||
@ -2482,4 +2448,41 @@ Smart Array P440 in Slot 2
|
||||
Primary Boot Volume: None
|
||||
Secondary Boot Volume: None
|
||||
|
||||
unassigned
|
||||
|
||||
physicaldrive 1I:2:1
|
||||
Drive Type: Unassigned Drive
|
||||
Interface Type: SAS
|
||||
Size: 300 GB
|
||||
Status: OK
|
||||
Drive Type: Unassigned Drive
|
||||
Sanitize Erase Supported: False
|
||||
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
|
||||
Unrestricted Sanitize Supported: False
|
||||
'''
|
||||
|
||||
SSA_ERASE_COMPLETE_NOT_SUPPORTED = '''
|
||||
|
||||
Smart Array P440 in Slot 2
|
||||
Controller Status: OK
|
||||
Firmware Version: 4.52-0
|
||||
Spare Activation Mode: Activate on physical drive failure (default)
|
||||
Controller Mode: RAID
|
||||
Pending Controller Mode: RAID
|
||||
Controller Mode Reboot: Not Required
|
||||
Sanitize Erase Supported: False
|
||||
Primary Boot Volume: None
|
||||
Secondary Boot Volume: None
|
||||
|
||||
unassigned
|
||||
|
||||
physicaldrive 1I:2:1
|
||||
Drive Type: Unassigned Drive
|
||||
Interface Type: SAS
|
||||
Size: 300 GB
|
||||
Status: Erase Complete. Reenable Before Using.
|
||||
Drive Type: Unassigned Drive
|
||||
Sanitize Erase Supported: False
|
||||
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
|
||||
Unrestricted Sanitize Supported: False
|
||||
'''
|
||||
|
@ -493,7 +493,9 @@ class ManagerTestCases(testtools.TestCase):
|
||||
'forced'])
|
||||
expt_ret = {
|
||||
'Smart Array P440 in Slot 2': {
|
||||
'1I:2:1': 'Erase Complete. Reenable Before Using.'}}
|
||||
'1I:2:1': 'Erase Complete. Reenable Before Using.',
|
||||
'Summary': ('Sanitize Erase performed on the disks attached to'
|
||||
' the controller.')}}
|
||||
get_all_details_mock.side_effect = [erase_drive, erase_complete,
|
||||
erase_complete]
|
||||
|
||||
@ -502,17 +504,6 @@ class ManagerTestCases(testtools.TestCase):
|
||||
controller_exec_cmd_mock.assert_any_call(*cmd_args)
|
||||
self.assertEqual(expt_ret, ret)
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_erase_devices_no_drives(self, controller_exec_cmd_mock,
|
||||
get_all_details_mock):
|
||||
erase_no_drives = raid_constants.SSA_ERASE_NOT_SUPPORTED
|
||||
get_all_details_mock.side_effect = [erase_no_drives]
|
||||
ex = self.assertRaises(exception.HPSSAOperationError,
|
||||
manager.erase_devices)
|
||||
expt_ret = ("None of the available SSA controllers Smart "
|
||||
"Array P440 in Slot 2 have Sanitize Erase Supported.")
|
||||
self.assertIn(expt_ret, str(ex))
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_erase_devices_in_progress(self, controller_exec_cmd_mock,
|
||||
get_all_details_mock):
|
||||
@ -521,7 +512,9 @@ class ManagerTestCases(testtools.TestCase):
|
||||
|
||||
expt_ret = {
|
||||
'Smart Array P440 in Slot 2': {
|
||||
'1I:2:1': 'Erase Complete. Reenable Before Using.'}}
|
||||
'1I:2:1': 'Erase Complete. Reenable Before Using.',
|
||||
'Summary': ('Sanitize Erase performed on the disks attached to'
|
||||
' the controller.')}}
|
||||
get_all_details_mock.side_effect = [erase_progress, erase_complete,
|
||||
erase_complete]
|
||||
|
||||
@ -529,6 +522,27 @@ class ManagerTestCases(testtools.TestCase):
|
||||
self.assertFalse(controller_exec_cmd_mock.called)
|
||||
self.assertEqual(expt_ret, ret)
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_erase_devices_not_supported(self, controller_exec_cmd_mock,
|
||||
get_all_details_mock):
|
||||
erase_not_supported = raid_constants.SSA_ERASE_NOT_SUPPORTED
|
||||
erase_complete = raid_constants.SSA_ERASE_COMPLETE_NOT_SUPPORTED
|
||||
get_all_details_mock.side_effect = [erase_not_supported,
|
||||
erase_complete, erase_complete]
|
||||
value = ("Drive 1I:2:1: This operation is not supported in this "
|
||||
"physical drive")
|
||||
controller_exec_cmd_mock.return_value = value
|
||||
expt_ret = {
|
||||
'Smart Array P440 in Slot 2': {
|
||||
'1I:2:1': 'Erase Complete. Reenable Before Using.',
|
||||
'Summary': ('Drives overwritten with zeros because '
|
||||
'sanitize erase is not supported on the '
|
||||
'controller.')
|
||||
}}
|
||||
|
||||
ret = manager.erase_devices()
|
||||
self.assertEqual(expt_ret, ret)
|
||||
|
||||
|
||||
class RaidConfigValidationTestCases(testtools.TestCase):
|
||||
|
||||
|
@ -344,13 +344,49 @@ class ControllerTest(testtools.TestCase):
|
||||
get_all_details_mock):
|
||||
get_all_details_mock.return_value = raid_constants.SSA_ERASE_DRIVE
|
||||
server = objects.Server()
|
||||
d = [x for x in server.controllers[0].unassigned_physical_drives]
|
||||
controller = server.controllers[0]
|
||||
controller.erase_devices('1I:2:1')
|
||||
controller.erase_devices(d)
|
||||
execute_mock.assert_called_once_with('pd 1I:2:1', 'modify', 'erase',
|
||||
'erasepattern=overwrite',
|
||||
'unrestricted=off',
|
||||
'forced')
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_erase_devices_sanitize_not_supported(self, execute_mock,
|
||||
get_all_details_mock):
|
||||
erase_not_supported = raid_constants.SSA_ERASE_NOT_SUPPORTED
|
||||
get_all_details_mock.return_value = erase_not_supported
|
||||
server = objects.Server()
|
||||
d = [x for x in server.controllers[0].unassigned_physical_drives]
|
||||
controller = server.controllers[0]
|
||||
value = ("Drive 1I:2:1: This operation is not supported in this "
|
||||
"physical drive")
|
||||
execute_mock.return_value = value
|
||||
controller.erase_devices(d)
|
||||
calls = [mock.call('pd 1I:2:1', 'modify', 'erase',
|
||||
'erasepattern=overwrite', 'unrestricted=off',
|
||||
'forced'),
|
||||
mock.call('pd 1I:2:1', 'modify', 'erase',
|
||||
'erasepattern=zero', 'forced')]
|
||||
execute_mock.assert_has_calls(calls)
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_erase_devices_exception(self, execute_mock,
|
||||
get_all_details_mock):
|
||||
get_all_details_mock.return_value = raid_constants.SSA_ERASE_DRIVE
|
||||
server = objects.Server()
|
||||
d = [x for x in server.controllers[0].unassigned_physical_drives]
|
||||
controller = server.controllers[0]
|
||||
value = 'Some Exception'
|
||||
execute_mock.side_effect = [exception.HPSSAOperationError(
|
||||
reason=value), None]
|
||||
|
||||
ex = self.assertRaises(exception.HPSSAOperationError,
|
||||
controller.erase_devices, d)
|
||||
|
||||
self.assertIn(value, str(ex))
|
||||
|
||||
|
||||
@mock.patch.object(objects.Server, '_get_all_details')
|
||||
class LogicalDriveTest(testtools.TestCase):
|
||||
|
@ -61,14 +61,19 @@ class ProliantHardwareManagerTestCase(testtools.TestCase):
|
||||
delete_mock.assert_called_once_with()
|
||||
self.assertEqual('current-config', ret)
|
||||
|
||||
@mock.patch.object(ironic_python_agent.hardware.GenericHardwareManager,
|
||||
'erase_devices')
|
||||
@mock.patch.object(hpssa_manager, 'erase_devices')
|
||||
def test_erase_devices(self, erase_mock):
|
||||
def test_erase_devices(self, erase_mock, generic_erase_mock):
|
||||
node = {}
|
||||
port = {}
|
||||
erase_mock.return_value = 'erase_status'
|
||||
generic_erase_mock.return_value = {'foo': 'bar'}
|
||||
ret = self.hardware_manager.erase_devices(node, port)
|
||||
erase_mock.assert_called_once_with()
|
||||
self.assertEqual({'Sanitize Erase': 'erase_status'}, ret)
|
||||
generic_erase_mock.assert_called_once_with(node, port)
|
||||
self.assertEqual({'Disk Erase Status': 'erase_status', 'foo': 'bar'},
|
||||
ret)
|
||||
|
||||
@mock.patch.object(ironic_python_agent.hardware.GenericHardwareManager,
|
||||
'erase_devices')
|
||||
@ -78,13 +83,11 @@ class ProliantHardwareManagerTestCase(testtools.TestCase):
|
||||
port = {}
|
||||
value = ("Sanitize erase not supported in the "
|
||||
"available controllers")
|
||||
e = exception.HPSSAOperationError(value)
|
||||
e = exception.HPSSAOperationError(reason=value)
|
||||
erase_mock.side_effect = e
|
||||
generic_erase_mock.return_value = {'foo': 'bar'}
|
||||
expt_return = {
|
||||
'Sanitize Erase': erase_mock.side_effect}
|
||||
expt_return.update(generic_erase_mock.return_value)
|
||||
|
||||
self.hardware_manager.erase_devices(node, port)
|
||||
exc = self.assertRaises(exception.HPSSAOperationError,
|
||||
self.hardware_manager.erase_devices,
|
||||
node, port)
|
||||
|
||||
generic_erase_mock.assert_called_once_with(node, port)
|
||||
self.assertIn(value, str(exc))
|
||||
|
Loading…
Reference in New Issue
Block a user