Add Sanitize disk erase support using SSA
This commit adds the implementation to perfrom sanitize disk erase on the disks attached to the Smart storage controller using ssacli. Closes-bug: #1648807 Change-Id: I2a015345a31113f9f9a95b7dd722eb9b3a6bda43
This commit is contained in:
@@ -22,6 +22,7 @@ from proliantutils import exception
|
|||||||
from proliantutils.hpssa import constants
|
from proliantutils.hpssa import constants
|
||||||
from proliantutils.hpssa import disk_allocator
|
from proliantutils.hpssa import disk_allocator
|
||||||
from proliantutils.hpssa import objects
|
from proliantutils.hpssa import objects
|
||||||
|
from proliantutils.ilo import common
|
||||||
|
|
||||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
RAID_CONFIG_SCHEMA = os.path.join(CURRENT_DIR, "raid_config_schema.json")
|
RAID_CONFIG_SCHEMA = os.path.join(CURRENT_DIR, "raid_config_schema.json")
|
||||||
@@ -74,27 +75,32 @@ def validate(raid_config):
|
|||||||
raise exception.InvalidInputError(msg)
|
raise exception.InvalidInputError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _filter_raid_mode_controllers(server):
|
def _select_controllers_by(server, select_condition, msg):
|
||||||
"""Filters out the hpssa controllers in raid mode.
|
"""Filters out the hpssa controllers based on the condition.
|
||||||
|
|
||||||
This method updates the server with only the controller which is in raid
|
This method updates the server with only the controller which satisfies
|
||||||
mode. The controller which are in HBA mode are removed from the list.
|
the condition. The controllers which doesn't satisfies the selection
|
||||||
|
condition will be removed from the list.
|
||||||
|
|
||||||
:param server: The object containing all the supported hpssa controllers
|
:param server: The object containing all the supported hpssa controllers
|
||||||
details.
|
details.
|
||||||
|
:param select_condition: A lambda function to select the controllers based
|
||||||
|
on requirement.
|
||||||
|
:param msg: A String which describes the controller selection.
|
||||||
:raises exception.HPSSAOperationError, if all the controller are in HBA
|
:raises exception.HPSSAOperationError, if all the controller are in HBA
|
||||||
mode.
|
mode.
|
||||||
"""
|
"""
|
||||||
all_controllers = server.controllers
|
all_controllers = server.controllers
|
||||||
non_hba_controllers = [c for c in all_controllers
|
supported_controllers = [c for c in all_controllers if select_condition(c)]
|
||||||
if not c.properties.get('HBA Mode Enabled', False)]
|
|
||||||
|
|
||||||
if not non_hba_controllers:
|
if not supported_controllers:
|
||||||
reason = ("None of the available HPSSA controllers %s have RAID "
|
reason = ("None of the available SSA controllers %(controllers)s "
|
||||||
"enabled" % ', '.join([c.id for c in all_controllers]))
|
"have %(msg)s"
|
||||||
|
% {'controllers': ', '.join([c.id for c in all_controllers]),
|
||||||
|
'msg': msg})
|
||||||
raise exception.HPSSAOperationError(reason=reason)
|
raise exception.HPSSAOperationError(reason=reason)
|
||||||
|
|
||||||
server.controllers = non_hba_controllers
|
server.controllers = supported_controllers
|
||||||
|
|
||||||
|
|
||||||
def create_configuration(raid_config):
|
def create_configuration(raid_config):
|
||||||
@@ -117,7 +123,9 @@ def create_configuration(raid_config):
|
|||||||
"""
|
"""
|
||||||
server = objects.Server()
|
server = objects.Server()
|
||||||
|
|
||||||
_filter_raid_mode_controllers(server)
|
select_controllers = lambda x: not x.properties.get('HBA Mode Enabled',
|
||||||
|
False)
|
||||||
|
_select_controllers_by(server, select_controllers, 'RAID enabled')
|
||||||
|
|
||||||
validate(raid_config)
|
validate(raid_config)
|
||||||
|
|
||||||
@@ -297,7 +305,9 @@ def delete_configuration():
|
|||||||
"""
|
"""
|
||||||
server = objects.Server()
|
server = objects.Server()
|
||||||
|
|
||||||
_filter_raid_mode_controllers(server)
|
select_controllers = lambda x: not x.properties.get('HBA Mode Enabled',
|
||||||
|
False)
|
||||||
|
_select_controllers_by(server, select_controllers, 'RAID enabled')
|
||||||
|
|
||||||
for controller in server.controllers:
|
for controller in server.controllers:
|
||||||
# Trigger delete only if there is some RAID array, otherwise
|
# Trigger delete only if there is some RAID array, otherwise
|
||||||
@@ -337,3 +347,56 @@ def get_configuration():
|
|||||||
|
|
||||||
_update_physical_disk_details(raid_config, server)
|
_update_physical_disk_details(raid_config, server)
|
||||||
return raid_config
|
return raid_config
|
||||||
|
|
||||||
|
|
||||||
|
def has_erase_completed():
|
||||||
|
server = objects.Server()
|
||||||
|
drives = server.get_physical_drives()
|
||||||
|
if any((drive.erase_status == 'Erase In Progress')
|
||||||
|
for drive in drives):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def erase_devices():
|
||||||
|
"""Erase all the drives on this server.
|
||||||
|
|
||||||
|
This method performs sanitize erase on all the supported physical drives
|
||||||
|
in this server. This erase cannot be performed on logical drives.
|
||||||
|
|
||||||
|
:returns: a dictionary of controllers with drives and the erase status.
|
||||||
|
:raises exception.HPSSAException, if none of the drives support
|
||||||
|
sanitize erase.
|
||||||
|
"""
|
||||||
|
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(
|
||||||
|
has_erase_completed,
|
||||||
|
delay_bw_retries=300,
|
||||||
|
failover_msg='Disk erase failed.'
|
||||||
|
)
|
||||||
|
|
||||||
|
server.refresh()
|
||||||
|
|
||||||
|
status = {}
|
||||||
|
for controller in server.controllers:
|
||||||
|
drive_status = {x.id: x.erase_status
|
||||||
|
for x in controller.unassigned_physical_drives}
|
||||||
|
status[controller.id] = drive_status
|
||||||
|
|
||||||
|
return status
|
||||||
|
|||||||
@@ -317,7 +317,12 @@ class Controller(object):
|
|||||||
self.unassigned_physical_drives = []
|
self.unassigned_physical_drives = []
|
||||||
self.raid_arrays = []
|
self.raid_arrays = []
|
||||||
|
|
||||||
unassigned_drives = properties.get('unassigned', {})
|
# This step is needed because of the mismatch in the data returned by
|
||||||
|
# hpssacli and ssacli.
|
||||||
|
attr = ''.join(x for x in properties
|
||||||
|
if x == 'Unassigned' or x == 'unassigned')
|
||||||
|
|
||||||
|
unassigned_drives = properties.get(attr, {})
|
||||||
for key, value in unassigned_drives.items():
|
for key, value in unassigned_drives.items():
|
||||||
self.unassigned_physical_drives.append(PhysicalDrive(key,
|
self.unassigned_physical_drives.append(PhysicalDrive(key,
|
||||||
value,
|
value,
|
||||||
@@ -407,6 +412,15 @@ class Controller(object):
|
|||||||
"""
|
"""
|
||||||
self.execute_cmd("logicaldrive", "all", "delete", "forced")
|
self.execute_cmd("logicaldrive", "all", "delete", "forced")
|
||||||
|
|
||||||
|
def erase_devices(self, drive):
|
||||||
|
cmd_args = []
|
||||||
|
cmd_args.append("pd %s" % drive)
|
||||||
|
cmd_args.extend(['modify', 'erase',
|
||||||
|
'erasepattern=overwrite',
|
||||||
|
'unrestricted=off',
|
||||||
|
'forced'])
|
||||||
|
self.execute_cmd(*cmd_args)
|
||||||
|
|
||||||
|
|
||||||
class RaidArray(object):
|
class RaidArray(object):
|
||||||
"""Class for a RAID Array.
|
"""Class for a RAID Array.
|
||||||
@@ -580,6 +594,7 @@ class PhysicalDrive:
|
|||||||
self.disk_type = constants.get_disk_type(ssa_interface)
|
self.disk_type = constants.get_disk_type(ssa_interface)
|
||||||
self.model = self.properties.get('Model')
|
self.model = self.properties.get('Model')
|
||||||
self.firmware = self.properties.get('Firmware Revision')
|
self.firmware = self.properties.get('Firmware Revision')
|
||||||
|
self.erase_status = self.properties.get('Status')
|
||||||
|
|
||||||
def get_physical_drive_dict(self):
|
def get_physical_drive_dict(self):
|
||||||
"""Returns a dictionary of with the details of the physical drive."""
|
"""Returns a dictionary of with the details of the physical drive."""
|
||||||
@@ -598,4 +613,5 @@ class PhysicalDrive:
|
|||||||
'interface_type': self.interface_type,
|
'interface_type': self.interface_type,
|
||||||
'model': self.model,
|
'model': self.model,
|
||||||
'firmware': self.firmware,
|
'firmware': self.firmware,
|
||||||
'status': status}
|
'status': status,
|
||||||
|
'erase_status': self.erase_status}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
from ironic_python_agent import hardware
|
from ironic_python_agent import hardware
|
||||||
|
|
||||||
|
from proliantutils import exception
|
||||||
from proliantutils.hpssa import manager as hpssa_manager
|
from proliantutils.hpssa import manager as hpssa_manager
|
||||||
|
|
||||||
|
|
||||||
@@ -80,3 +81,21 @@ class ProliantHardwareManager(hardware.GenericHardwareManager):
|
|||||||
for the node
|
for the node
|
||||||
"""
|
"""
|
||||||
return hpssa_manager.delete_configuration()
|
return hpssa_manager.delete_configuration()
|
||||||
|
|
||||||
|
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.
|
||||||
|
: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))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -2330,3 +2330,156 @@ Smart Array P822 in Slot 3
|
|||||||
Mount Points: None
|
Mount Points: None
|
||||||
Sanitize Erase Supported: False
|
Sanitize Erase Supported: False
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
SSA_ERASE_DRIVE = '''
|
||||||
|
|
||||||
|
Smart Array P440 in Slot 2
|
||||||
|
Bus Interface: PCI
|
||||||
|
Slot: 2
|
||||||
|
Serial Number: PDNMF0ARH8Y342
|
||||||
|
RAID 6 (ADG) Status: Enabled
|
||||||
|
Controller Status: OK
|
||||||
|
Firmware Version: 4.52-0
|
||||||
|
Spare Activation Mode: Activate on physical drive failure (default)
|
||||||
|
Encryption: Disabled
|
||||||
|
Driver Name: hpsa
|
||||||
|
Driver Version: 3.4.16
|
||||||
|
Controller Mode: RAID
|
||||||
|
Pending Controller Mode: RAID
|
||||||
|
Controller Mode Reboot: Not Required
|
||||||
|
Host Serial Number: SGH537Y7AY
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
Primary Boot Volume: None
|
||||||
|
Secondary Boot Volume: None
|
||||||
|
|
||||||
|
|
||||||
|
Port Name: 1I
|
||||||
|
Port ID: 0
|
||||||
|
Port Connection Number: 0
|
||||||
|
SAS Address: 5001438035544EC0
|
||||||
|
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
|
||||||
|
Port: 1I
|
||||||
|
Box: 2
|
||||||
|
Bay: 1
|
||||||
|
Status: OK
|
||||||
|
Drive Type: Unassigned Drive
|
||||||
|
Interface Type: SAS
|
||||||
|
Size: 300 GB
|
||||||
|
Drive exposed to OS: False
|
||||||
|
Logical/Physical Block Size: 512/512
|
||||||
|
Rotational Speed: 15100
|
||||||
|
Firmware Revision: HPD4
|
||||||
|
Serial Number: S7K0C3FJ0000K601EZLM
|
||||||
|
WWID: 5000C5008E183B1D
|
||||||
|
Model: HP EH0300JEDHC
|
||||||
|
Current Temperature (C): 42
|
||||||
|
Maximum Temperature (C): 52
|
||||||
|
PHY Count: 2
|
||||||
|
PHY Transfer Rate: 12.0Gbps, Unknown
|
||||||
|
Drive Authentication Status: OK
|
||||||
|
Carrier Application Version: 11
|
||||||
|
Carrier Bootloader Version: 6
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
|
||||||
|
Unrestricted Sanitize Supported: False
|
||||||
|
Shingled Magnetic Recording Support: None
|
||||||
|
'''
|
||||||
|
|
||||||
|
SSA_ERASE_IN_PROGRESS = '''
|
||||||
|
Smart Array P440 in Slot 2
|
||||||
|
Controller Mode: RAID
|
||||||
|
Pending Controller Mode: RAID
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
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
|
||||||
|
Drive Type: Unassigned Drive
|
||||||
|
Interface Type: SAS
|
||||||
|
Size: 300 GB
|
||||||
|
Status: Erase In Progress
|
||||||
|
Drive Type: Unassigned Drive
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
|
||||||
|
Unrestricted Sanitize Supported: False
|
||||||
|
'''
|
||||||
|
|
||||||
|
SSA_ERASE_COMPLETE = '''
|
||||||
|
Smart Array P440 in Slot 2
|
||||||
|
Controller Mode: RAID
|
||||||
|
Pending Controller Mode: RAID
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
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
|
||||||
|
Drive Type: Unassigned Drive
|
||||||
|
Interface Type: SAS
|
||||||
|
Size: 300 GB
|
||||||
|
Status: Erase Complete. Reenable Before Using.
|
||||||
|
Drive Type: Unassigned Drive
|
||||||
|
Sanitize Erase Supported: True
|
||||||
|
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
|
||||||
|
Unrestricted Sanitize Supported: False
|
||||||
|
'''
|
||||||
|
|
||||||
|
SSA_ERASE_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
|
||||||
|
|
||||||
|
'''
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class ManagerTestCases(testtools.TestCase):
|
|||||||
raid_info = {'logical_disks': 'foo'}
|
raid_info = {'logical_disks': 'foo'}
|
||||||
|
|
||||||
msg = ("An error was encountered while doing hpssa configuration: None"
|
msg = ("An error was encountered while doing hpssa configuration: None"
|
||||||
" of the available HPSSA controllers Smart Array P822 in "
|
" of the available SSA controllers Smart Array P822 in "
|
||||||
"Slot 3 have RAID enabled")
|
"Slot 3 have RAID enabled")
|
||||||
ex = self.assertRaises(exception.HPSSAOperationError,
|
ex = self.assertRaises(exception.HPSSAOperationError,
|
||||||
manager.create_configuration,
|
manager.create_configuration,
|
||||||
@@ -373,7 +373,6 @@ class ManagerTestCases(testtools.TestCase):
|
|||||||
def test_delete_configuration(self, controller_exec_cmd_mock,
|
def test_delete_configuration(self, controller_exec_cmd_mock,
|
||||||
get_configuration_mock,
|
get_configuration_mock,
|
||||||
get_all_details_mock):
|
get_all_details_mock):
|
||||||
|
|
||||||
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
|
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
|
||||||
get_configuration_mock.return_value = 'foo'
|
get_configuration_mock.return_value = 'foo'
|
||||||
|
|
||||||
@@ -406,7 +405,7 @@ class ManagerTestCases(testtools.TestCase):
|
|||||||
get_all_details_mock.return_value = drives
|
get_all_details_mock.return_value = drives
|
||||||
|
|
||||||
msg = ("An error was encountered while doing hpssa configuration: None"
|
msg = ("An error was encountered while doing hpssa configuration: None"
|
||||||
" of the available HPSSA controllers Smart Array P822 in "
|
" of the available SSA controllers Smart Array P822 in "
|
||||||
"Slot 3 have RAID enabled")
|
"Slot 3 have RAID enabled")
|
||||||
ex = self.assertRaises(exception.HPSSAOperationError,
|
ex = self.assertRaises(exception.HPSSAOperationError,
|
||||||
manager.delete_configuration)
|
manager.delete_configuration)
|
||||||
@@ -454,28 +453,82 @@ class ManagerTestCases(testtools.TestCase):
|
|||||||
self.assertEqual(sorted(pds_active_expected), sorted(pds_active))
|
self.assertEqual(sorted(pds_active_expected), sorted(pds_active))
|
||||||
self.assertEqual(sorted(pds_ready_expected), sorted(pds_ready))
|
self.assertEqual(sorted(pds_ready_expected), sorted(pds_ready))
|
||||||
|
|
||||||
def test__filter_raid_mode_controllers_hba(self, get_all_details_mock):
|
def test__select_controllers_by_hba(self, get_all_details_mock):
|
||||||
get_all_details_mock.return_value = raid_constants.HPSSA_HBA_MODE
|
get_all_details_mock.return_value = raid_constants.HPSSA_HBA_MODE
|
||||||
|
|
||||||
server = objects.Server()
|
server = objects.Server()
|
||||||
|
select_controllers = lambda x: not x.properties.get('HBA Mode Enabled',
|
||||||
|
False)
|
||||||
|
|
||||||
msg = ("An error was encountered while doing hpssa configuration: None"
|
msg = ("An error was encountered while doing hpssa configuration: "
|
||||||
" of the available HPSSA controllers Smart Array P822 in "
|
"None of the available SSA controllers Smart Array P822 in "
|
||||||
"Slot 3 have RAID enabled")
|
"Slot 3 have Raid enabled.")
|
||||||
ex = self.assertRaises(exception.HPSSAOperationError,
|
ex = self.assertRaises(exception.HPSSAOperationError,
|
||||||
manager._filter_raid_mode_controllers,
|
manager._select_controllers_by,
|
||||||
server)
|
server, select_controllers, 'Raid enabled')
|
||||||
self.assertIn(msg, str(ex))
|
self.assertIn(msg, str(ex))
|
||||||
|
|
||||||
def test__filter_raid_mode_controllers(self, get_all_details_mock):
|
def test__select_controllers_by(self, get_all_details_mock):
|
||||||
get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES
|
get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES
|
||||||
|
|
||||||
server = objects.Server()
|
server = objects.Server()
|
||||||
|
select_controllers = lambda x: not x.properties.get('HBA Mode Enabled',
|
||||||
|
False)
|
||||||
ctrl_expected = server.controllers
|
ctrl_expected = server.controllers
|
||||||
|
|
||||||
manager._filter_raid_mode_controllers(server)
|
manager._select_controllers_by(server, select_controllers,
|
||||||
|
'Raid enabled')
|
||||||
self.assertEqual(ctrl_expected, server.controllers)
|
self.assertEqual(ctrl_expected, server.controllers)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||||
|
def test_erase_devices(self, controller_exec_cmd_mock,
|
||||||
|
get_all_details_mock):
|
||||||
|
erase_drive = raid_constants.SSA_ERASE_DRIVE
|
||||||
|
erase_complete = raid_constants.SSA_ERASE_COMPLETE
|
||||||
|
cmd_args = []
|
||||||
|
cmd_args.append("pd 1I:2:1")
|
||||||
|
cmd_args.extend(['modify', 'erase',
|
||||||
|
'erasepattern=overwrite',
|
||||||
|
'unrestricted=off',
|
||||||
|
'forced'])
|
||||||
|
expt_ret = {
|
||||||
|
'Smart Array P440 in Slot 2': {
|
||||||
|
'1I:2:1': 'Erase Complete. Reenable Before Using.'}}
|
||||||
|
get_all_details_mock.side_effect = [erase_drive, erase_complete,
|
||||||
|
erase_complete]
|
||||||
|
|
||||||
|
ret = manager.erase_devices()
|
||||||
|
self.assertTrue(controller_exec_cmd_mock.called)
|
||||||
|
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):
|
||||||
|
erase_progress = raid_constants.SSA_ERASE_IN_PROGRESS
|
||||||
|
erase_complete = raid_constants.SSA_ERASE_COMPLETE
|
||||||
|
|
||||||
|
expt_ret = {
|
||||||
|
'Smart Array P440 in Slot 2': {
|
||||||
|
'1I:2:1': 'Erase Complete. Reenable Before Using.'}}
|
||||||
|
get_all_details_mock.side_effect = [erase_progress, erase_complete,
|
||||||
|
erase_complete]
|
||||||
|
|
||||||
|
ret = manager.erase_devices()
|
||||||
|
self.assertFalse(controller_exec_cmd_mock.called)
|
||||||
|
self.assertEqual(expt_ret, ret)
|
||||||
|
|
||||||
|
|
||||||
class RaidConfigValidationTestCases(testtools.TestCase):
|
class RaidConfigValidationTestCases(testtools.TestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -337,6 +337,18 @@ class ControllerTest(testtools.TestCase):
|
|||||||
|
|
||||||
self.assertIsNone(controller.get_physical_drive_by_id('foo'))
|
self.assertIsNone(controller.get_physical_drive_by_id('foo'))
|
||||||
|
|
||||||
|
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||||
|
def test_erase_devices(self, execute_mock,
|
||||||
|
get_all_details_mock):
|
||||||
|
get_all_details_mock.return_value = raid_constants.SSA_ERASE_DRIVE
|
||||||
|
server = objects.Server()
|
||||||
|
controller = server.controllers[0]
|
||||||
|
controller.erase_devices('1I:2:1')
|
||||||
|
execute_mock.assert_called_once_with('pd 1I:2:1', 'modify', 'erase',
|
||||||
|
'erasepattern=overwrite',
|
||||||
|
'unrestricted=off',
|
||||||
|
'forced')
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(objects.Server, '_get_all_details')
|
@mock.patch.object(objects.Server, '_get_all_details')
|
||||||
class LogicalDriveTest(testtools.TestCase):
|
class LogicalDriveTest(testtools.TestCase):
|
||||||
@@ -516,6 +528,8 @@ class PhysicalDriveTest(testtools.TestCase):
|
|||||||
self.assertEqual('ssd', ret_sata['disk_type'])
|
self.assertEqual('ssd', ret_sata['disk_type'])
|
||||||
self.assertEqual('sata', ret_sata['interface_type'])
|
self.assertEqual('sata', ret_sata['interface_type'])
|
||||||
|
|
||||||
|
self.assertEqual('OK', ret_sata['erase_status'])
|
||||||
|
|
||||||
def test_get_physical_drive_dict_part_of_array(self, get_all_details_mock):
|
def test_get_physical_drive_dict_part_of_array(self, get_all_details_mock):
|
||||||
|
|
||||||
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
|
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
|
||||||
@@ -532,6 +546,7 @@ class PhysicalDriveTest(testtools.TestCase):
|
|||||||
self.assertEqual('HP EF0600FARNA', ret['model'])
|
self.assertEqual('HP EF0600FARNA', ret['model'])
|
||||||
self.assertEqual('HPD6', ret['firmware'])
|
self.assertEqual('HPD6', ret['firmware'])
|
||||||
self.assertEqual('active', ret['status'])
|
self.assertEqual('active', ret['status'])
|
||||||
|
self.assertEqual('OK', ret['erase_status'])
|
||||||
|
|
||||||
def test_get_physical_drive_dict_unassigned(self, get_all_details_mock):
|
def test_get_physical_drive_dict_unassigned(self, get_all_details_mock):
|
||||||
|
|
||||||
@@ -549,6 +564,7 @@ class PhysicalDriveTest(testtools.TestCase):
|
|||||||
self.assertEqual('HP EF0600FARNA', ret['model'])
|
self.assertEqual('HP EF0600FARNA', ret['model'])
|
||||||
self.assertEqual('HPD6', ret['firmware'])
|
self.assertEqual('HPD6', ret['firmware'])
|
||||||
self.assertEqual('ready', ret['status'])
|
self.assertEqual('ready', ret['status'])
|
||||||
|
self.assertEqual('OK', ret['erase_status'])
|
||||||
|
|
||||||
|
|
||||||
class PrivateMethodsTestCase(testtools.TestCase):
|
class PrivateMethodsTestCase(testtools.TestCase):
|
||||||
|
|||||||
@@ -24,5 +24,6 @@ if not ironic_python_agent:
|
|||||||
sys.modules['ironic_python_agent.errors'] = ipa_mock.errors
|
sys.modules['ironic_python_agent.errors'] = ipa_mock.errors
|
||||||
sys.modules['ironic_python_agent.hardware'] = ipa_mock.hardware
|
sys.modules['ironic_python_agent.hardware'] = ipa_mock.hardware
|
||||||
ipa_mock.hardware.GenericHardwareManager = mock.MagicMock
|
ipa_mock.hardware.GenericHardwareManager = mock.MagicMock
|
||||||
|
mock.MagicMock.erase_devices = mock.MagicMock(name='erase_devices')
|
||||||
if 'proliantutils.ipa_hw_manager' in sys.modules:
|
if 'proliantutils.ipa_hw_manager' in sys.modules:
|
||||||
reload(sys.modules['proliantutils.ipa_hw_manager'])
|
reload(sys.modules['proliantutils.ipa_hw_manager'])
|
||||||
|
|||||||
@@ -13,11 +13,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from oslo_utils import importutils
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
from proliantutils import exception
|
||||||
from proliantutils.hpssa import manager as hpssa_manager
|
from proliantutils.hpssa import manager as hpssa_manager
|
||||||
from proliantutils.ipa_hw_manager import hardware_manager
|
from proliantutils.ipa_hw_manager import hardware_manager
|
||||||
|
|
||||||
|
ironic_python_agent = importutils.try_import('ironic_python_agent')
|
||||||
|
|
||||||
|
|
||||||
class ProliantHardwareManagerTestCase(testtools.TestCase):
|
class ProliantHardwareManagerTestCase(testtools.TestCase):
|
||||||
|
|
||||||
@@ -50,3 +54,31 @@ class ProliantHardwareManagerTestCase(testtools.TestCase):
|
|||||||
ret = self.hardware_manager.delete_configuration("", "")
|
ret = self.hardware_manager.delete_configuration("", "")
|
||||||
delete_mock.assert_called_once_with()
|
delete_mock.assert_called_once_with()
|
||||||
self.assertEqual('current-config', ret)
|
self.assertEqual('current-config', ret)
|
||||||
|
|
||||||
|
@mock.patch.object(hpssa_manager, 'erase_devices')
|
||||||
|
def test_erase_devices(self, erase_mock):
|
||||||
|
node = {}
|
||||||
|
port = {}
|
||||||
|
erase_mock.return_value = 'erase_status'
|
||||||
|
ret = self.hardware_manager.erase_devices(node, port)
|
||||||
|
erase_mock.assert_called_once_with()
|
||||||
|
self.assertEqual({'Sanitize Erase': 'erase_status'}, ret)
|
||||||
|
|
||||||
|
@mock.patch.object(ironic_python_agent.hardware.GenericHardwareManager,
|
||||||
|
'erase_devices')
|
||||||
|
@mock.patch.object(hpssa_manager, 'erase_devices')
|
||||||
|
def test_erase_devices_not_supported(self, erase_mock, generic_erase_mock):
|
||||||
|
node = {}
|
||||||
|
port = {}
|
||||||
|
value = ("Sanitize erase not supported in the "
|
||||||
|
"available controllers")
|
||||||
|
e = exception.HPSSAOperationError(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)
|
||||||
|
|
||||||
|
generic_erase_mock.assert_called_once_with(node, port)
|
||||||
|
|||||||
Reference in New Issue
Block a user