HPSSA: Add support for sharing physical disks

This commit adds support for logical disks to share
physical disks if explicitly requested.

Implements: blueprint hpssa-support
Change-Id: I379a199726e81b37456d4ec2d413aa6e65f20174
This commit is contained in:
Ramakrishnan G
2014-11-03 09:04:53 +00:00
parent ebc262e2d0
commit aed12ab5b3
7 changed files with 791 additions and 50 deletions

View File

@@ -32,19 +32,19 @@ def _get_criteria_matching_disks(logical_disk, physical_drives):
criteria_to_consider = [x for x in FILTER_CRITERIA criteria_to_consider = [x for x in FILTER_CRITERIA
if x in logical_disk] if x in logical_disk]
for physical_drive in physical_drives: for physical_drive_object in physical_drives:
for criteria in criteria_to_consider: for criteria in criteria_to_consider:
logical_drive_value = logical_disk.get(criteria) logical_drive_value = logical_disk.get(criteria)
physical_drive_value = getattr(physical_drive, criteria) physical_drive_value = getattr(physical_drive_object, criteria)
if logical_drive_value != physical_drive_value: if logical_drive_value != physical_drive_value:
break break
else: else:
matching_physical_drives.append(physical_drive) matching_physical_drives.append(physical_drive_object)
return matching_physical_drives return matching_physical_drives
def allocate_disks(logical_disk, server): def allocate_disks(logical_disk, server, raid_config):
"""Allocate physical disks to a logical disk. """Allocate physical disks to a logical disk.
This method allocated physical disks to a logical This method allocated physical disks to a logical
@@ -54,6 +54,7 @@ def allocate_disks(logical_disk, server):
:param logical_disk: a dictionary of a logical disk :param logical_disk: a dictionary of a logical disk
from the RAID configuration input to the module. from the RAID configuration input to the module.
:param server: An objects.Server object :param server: An objects.Server object
:param raid_config: The target RAID configuration requested.
:raises: PhysicalDisksNotFoundError, if cannot find :raises: PhysicalDisksNotFoundError, if cannot find
physical disks for the request. physical disks for the request.
""" """
@@ -63,6 +64,7 @@ def allocate_disks(logical_disk, server):
'number_of_physical_disks', constants.RAID_LEVEL_MIN_DISKS[raid_level]) 'number_of_physical_disks', constants.RAID_LEVEL_MIN_DISKS[raid_level])
share_physical_disks = logical_disk.get('share_physical_disks', False) share_physical_disks = logical_disk.get('share_physical_disks', False)
# Try to create a new independent array for this request.
for controller in server.controllers: for controller in server.controllers:
physical_drives = controller.unassigned_physical_drives physical_drives = controller.unassigned_physical_drives
physical_drives = _get_criteria_matching_disks(logical_disk, physical_drives = _get_criteria_matching_disks(logical_disk,
@@ -76,13 +78,39 @@ def allocate_disks(logical_disk, server):
logical_disk['controller'] = controller.id logical_disk['controller'] = controller.id
physical_disks = selected_drive_ids[:number_of_physical_disks] physical_disks = selected_drive_ids[:number_of_physical_disks]
logical_disk['physical_disks'] = physical_disks logical_disk['physical_disks'] = physical_disks
break return
if not share_physical_disks: # We didn't find physical disks to create an independent array.
# TODO(rameshg87): When this logical drives can share disks # Check if we can get some shared arrays.
# with other arrays, figure out free space in other arrays if share_physical_disks:
# and then consider which array to use. sharable_disk_wwns = []
pass for sharable_logical_disk in raid_config['logical_disks']:
else: if (sharable_logical_disk.get('share_physical_disks', False) and
raise exception.PhysicalDisksNotFoundError(size_gb=size_gb, 'root_device_hint' in sharable_logical_disk):
raid_level=raid_level) wwn = sharable_logical_disk['root_device_hint']['wwn']
sharable_disk_wwns.append(wwn)
for controller in server.controllers:
sharable_arrays = [x for x in controller.raid_arrays if
x.logical_drives[0].wwn in sharable_disk_wwns]
for array in sharable_arrays:
# Check if criterias for the logical disk match the ones with
# physical disks in the raid array.
criteria_matched_disks = _get_criteria_matching_disks(
logical_disk, array.physical_drives)
# Check if all disks in the array don't match the criteria
if len(criteria_matched_disks) != len(array.physical_drives):
continue
# Check if raid array can accomodate the logical disk.
if array.can_accomodate(logical_disk):
logical_disk['controller'] = controller.id
logical_disk['array'] = array.id
return
# We check both options and couldn't get any physical disks.
raise exception.PhysicalDisksNotFoundError(size_gb=size_gb,
raid_level=raid_level)

View File

@@ -116,7 +116,8 @@ def create_configuration(raid_config):
for logical_disk in logical_disks_sorted: for logical_disk in logical_disks_sorted:
if 'physical_disks' not in logical_disk: if 'physical_disks' not in logical_disk:
disk_allocator.allocate_disks(logical_disk, server) disk_allocator.allocate_disks(logical_disk, server,
raid_config)
controller_id = logical_disk['controller'] controller_id = logical_disk['controller']
@@ -125,18 +126,17 @@ def create_configuration(raid_config):
msg = ("Unable to find controller named '%s'" % controller_id) msg = ("Unable to find controller named '%s'" % controller_id)
raise exception.InvalidInputError(reason=msg) raise exception.InvalidInputError(reason=msg)
for physical_disk in logical_disk['physical_disks']: if 'physical_disks' in logical_disk:
disk_obj = controller.get_physical_drive_by_id(physical_disk) for physical_disk in logical_disk['physical_disks']:
if not disk_obj: disk_obj = controller.get_physical_drive_by_id(physical_disk)
msg = ("Unable to find physical disk '%(physical_disk)s' " if not disk_obj:
"on '%(controller)s'" % msg = ("Unable to find physical disk '%(physical_disk)s' "
{'physical_disk': physical_disk, "on '%(controller)s'" %
'controller': controller_id}) {'physical_disk': physical_disk,
raise exception.InvalidInputError(msg) 'controller': controller_id})
raise exception.InvalidInputError(msg)
physical_drive_ids = logical_disk['physical_disks'] controller.create_logical_drive(logical_disk)
controller.create_logical_drive(logical_disk, physical_drive_ids)
# Now find the new logical drive created. # Now find the new logical drive created.
server.refresh() server.refresh()

View File

@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import re
import time import time
from oslo.concurrency import processutils from oslo.concurrency import processutils
@@ -115,13 +116,36 @@ def _convert_to_dict(stdout):
return info_dict return info_dict
def _hpssacli(*args): def _hpssacli(*args, **kwargs):
"""Wrapper function for executing hpssacli command.""" """Wrapper function for executing hpssacli command.
:param args: args to be provided to hpssacli command
:param kwargs: kwargs to be sent to processutils except the
following:
- dont_transform_to_hpssa_exception - Set to True if this
method shouldn't transform other exceptions to hpssa
exceptions. This is useful when the return code from hpssacli
is useful for analysis.
:returns: a tuple containing the stdout and stderr after running
the process.
:raises: HPSSAOperationError, if some error was encountered and
dont_dont_transform_to_hpssa_exception was set to False.
:raises: OSError or processutils.ProcessExecutionError if execution
failed and dont_dont_transform_to_hpssa_exception was set to True.
"""
dont_transform_to_hpssa_exception = kwargs.get(
'dont_transform_to_hpssa_exception', False)
kwargs.pop('dont_transform_to_hpssa_exception', None)
try: try:
stdout, stderr = processutils.execute("hpssacli", stdout, stderr = processutils.execute("hpssacli",
*args) *args, **kwargs)
except (OSError, processutils.ProcessExecutionError) as e: except (OSError, processutils.ProcessExecutionError) as e:
raise exception.HPSSAOperationError(reason=e) if not dont_transform_to_hpssa_exception:
raise exception.HPSSAOperationError(reason=e)
else:
raise
return stdout, stderr return stdout, stderr
@@ -318,22 +342,23 @@ class Controller(object):
return phy_drive return phy_drive
return None return None
def execute_cmd(self, *args): def execute_cmd(self, *args, **kwargs):
"""Execute a given hpssacli command on the controller. """Execute a given hpssacli command on the controller.
This method executes a given command on the controller. This method executes a given command on the controller.
:params args: a tuple consisting of sub-commands to be appended :params args: a tuple consisting of sub-commands to be appended
after specifying the controller in hpssacli command. after specifying the controller in hpssacli command.
:param kwargs: kwargs to be passed to execute() in processutils
:raises: HPSSAOperationError, if hpssacli operation failed. :raises: HPSSAOperationError, if hpssacli operation failed.
""" """
slot = self.properties['Slot'] slot = self.properties['Slot']
base_cmd = ("controller", "slot=%s" % slot) base_cmd = ("controller", "slot=%s" % slot)
cmd = base_cmd + args cmd = base_cmd + args
return _hpssacli(*cmd) return _hpssacli(*cmd, **kwargs)
def create_logical_drive(self, logical_drive_info, physical_drive_ids): def create_logical_drive(self, logical_drive_info):
"""Create a logical drive on the controller. """Create a logical drive on the controller.
This method creates a logical drive on the controller when the This method creates a logical drive on the controller when the
@@ -341,10 +366,18 @@ class Controller(object):
:param logical_drive_info: a dictionary containing the details :param logical_drive_info: a dictionary containing the details
of the logical drive as specified in raid config. of the logical drive as specified in raid config.
:param physical_drive_ids: a list of physical drive ids to be used.
:raises: HPSSAOperationError, if hpssacli operation failed. :raises: HPSSAOperationError, if hpssacli operation failed.
""" """
phy_drive_ids = ','.join(physical_drive_ids) cmd_args = []
if 'array' in logical_drive_info:
cmd_args.extend(['array', logical_drive_info['array']])
cmd_args.extend(['create', "type=logicaldrive"])
if 'physical_disks' in logical_drive_info:
phy_drive_ids = ','.join(logical_drive_info['physical_disks'])
cmd_args.append("drives=%s" % phy_drive_ids)
size_mb = logical_drive_info['size_gb'] * 1024 size_mb = logical_drive_info['size_gb'] * 1024
raid_level = logical_drive_info['raid_level'] raid_level = logical_drive_info['raid_level']
@@ -352,10 +385,9 @@ class Controller(object):
# Check if we have mapping stored, otherwise use the same. # Check if we have mapping stored, otherwise use the same.
raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get( raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get(
raid_level, raid_level) raid_level, raid_level)
self.execute_cmd("create", "type=logicaldrive", cmd_args.extend(["raid=%s" % raid_level, "size=%s" % size_mb])
"drives=%s" % phy_drive_ids,
"raid=%s" % raid_level, self.execute_cmd(*cmd_args)
"size=%s" % size_mb)
def delete_all_logical_drives(self): def delete_all_logical_drives(self):
"""Deletes all logical drives on trh controller. """Deletes all logical drives on trh controller.
@@ -395,6 +427,48 @@ class RaidArray(object):
properties[physical_drive], properties[physical_drive],
self)) self))
def can_accomodate(self, logical_disk):
"""Check if this RAID array can accomodate the logical disk.
This method uses hpssacli command's option to check if the
logical disk with desired size and RAID level can be created
on this RAID array.
:param logical_disk: Dictionary of logical disk to be created.
:returns: True, if logical disk can be created on the RAID array
False, otherwise.
"""
raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get(
logical_disk['raid_level'], logical_disk['raid_level'])
args = ("array", self.id, "create", "type=logicaldrive",
"raid=%s" % raid_level, "size=?")
try:
stdout, stderr = self.parent.execute_cmd(
*args, dont_transform_to_hpssa_exception=True)
except processutils.ProcessExecutionError as ex:
# hpssacli returns error code 1 when RAID level of the
# logical disk is not supported on the array.
# If that's the case, just return saying the logical disk
# cannot be accomodated in the array.
# If exist_code is not 1, then it's some other error that we
# don't expect to appear and hence raise it back.
if ex.exit_code == 1:
return False
else:
raise exception.HPSSAOperationError(reason=ex)
except Exception as ex:
raise exception.HPSSAOperationError(reason=ex)
# TODO(rameshg87): This always returns in MB, but confirm with
# HPSSA folks.
match = re.search('Max: (\d+)', stdout)
if not match:
return False
max_size_gb = int(match.group(1)) / 1024
return logical_disk['size_gb'] <= max_size_gb
class LogicalDrive(object): class LogicalDrive(object):
"""Class for LogicalDrive object.""" """Class for LogicalDrive object."""

View File

@@ -1411,3 +1411,383 @@ Smart Array P822 in Slot 2
Carrier Application Version: 11 Carrier Application Version: 11
Carrier Bootloader Version: 6 Carrier Bootloader Version: 6
''' '''
ARRAY_ACCOMODATE_LOGICAL_DISK = '''
Available options are:
Max: 1042188 (Units in MB)
Min: 16 (Units in MB)
'''
ARRAY_ACCOMODATE_LOGICAL_DISK_INVALID = '''
Error: "raid=1" is not a valid option for array A
Available options are:
0
1adm
5 (default value)
'''
HPSSA_NO_DRIVES_2_PHYSICAL_DISKS = '''
Smart Array P822 in Slot 2
Bus Interface: PCI
Slot: 2
Serial Number: PDVTF0BRH5T0MO
Cache Serial Number: PBKUD0BRH5T3I6
RAID 6 (ADG) Status: Enabled
Controller Status: OK
Hardware Revision: B
Firmware Version: 4.68
Wait for Cache Room: Disabled
Surface Analysis Inconsistency Notification: Disabled
Post Prompt Timeout: 15 secs
Cache Board Present: True
Cache Status: OK
Drive Write Cache: Disabled
Total Cache Size: 2.0 GB
Total Cache Memory Available: 1.8 GB
No-Battery Write Cache: Disabled
Cache Backup Power Source: Capacitors
Battery/Capacitor Count: 1
Battery/Capacitor Status: OK
SATA NCQ Supported: True
Spare Activation Mode: Activate on physical drive failure (default)
Controller Temperature (C): 88
Cache Module Temperature (C): 37
Capacitor Temperature (C): 21
Number of Ports: 6 (2 Internal / 4 External )
Driver Name: hpsa
Driver Version: 3.4.4
Driver Supports HP SSD Smart Path: True
unassigned
physicaldrive 5I:1:1
Port: 5I
Box: 1
Bay: 1
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
Serial Number: 6SL7G55D0000N4173JLT
Model: HP EF0600FARNA
Current Temperature (C): 35
Maximum Temperature (C): 43
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
physicaldrive 5I:1:2
Port: 5I
Box: 1
Bay: 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
Serial Number: 6SL7H2DM0000B41800Y0
Model: HP EF0600FARNA
Current Temperature (C): 35
Maximum Temperature (C): 44
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
SEP (Vendor ID PMCSIERA, Model SRCv24x6G) 380
Device Number: 380
Firmware Version: RevB
WWID: 5001438028842E1F
Vendor ID: PMCSIERA
Model: SRCv24x6G
'''
ONE_DRIVE_RAID_1 = '''
Smart Array P822 in Slot 2
Bus Interface: PCI
Slot: 2
Serial Number: PDVTF0BRH5T0MO
Cache Serial Number: PBKUD0BRH5T3I6
RAID 6 (ADG) Status: Enabled
Controller Status: OK
Hardware Revision: B
Firmware Version: 4.68
Rebuild Priority: Medium
Expand Priority: Medium
Surface Scan Delay: 3 secs
Surface Scan Mode: Idle
Queue Depth: Automatic
Monitor and Performance Delay: 60 min
Elevator Sort: Enabled
Degraded Performance Optimization: Disabled
Inconsistency Repair Policy: Disabled
Wait for Cache Room: Disabled
Surface Analysis Inconsistency Notification: Disabled
Post Prompt Timeout: 15 secs
Cache Board Present: True
Cache Status: OK
Cache Ratio: 10% Read / 90% Write
Drive Write Cache: Disabled
Total Cache Size: 2.0 GB
Total Cache Memory Available: 1.8 GB
No-Battery Write Cache: Disabled
Cache Backup Power Source: Capacitors
Battery/Capacitor Count: 1
Battery/Capacitor Status: OK
SATA NCQ Supported: True
Spare Activation Mode: Activate on physical drive failure (default)
Controller Temperature (C): 88
Cache Module Temperature (C): 38
Capacitor Temperature (C): 23
Number of Ports: 6 (2 Internal / 4 External )
Driver Name: hpsa
Driver Version: 3.4.4
Driver Supports HP SSD Smart Path: True
Array: A
Interface Type: SAS
Unused Space: 1042189 MB
Status: OK
MultiDomain Status: OK
Array Type: Data
HP SSD Smart Path: disable
Logical Drive: 1
Size: 50.0 GB
Fault Tolerance: 1
Heads: 255
Sectors Per Track: 32
Cylinders: 12850
Strip Size: 256 KB
Full Stripe Size: 256 KB
Status: OK
MultiDomain Status: OK
Caching: Enabled
Unique Identifier: 600508B1001C02BDBCB659B8A264186A
Disk Name: /dev/sda
Mount Points: None
Logical Drive Label: 02896A0EPDVTF0BRH5T0MOEBAA
Mirror Group 0:
physicaldrive 5I:1:1 (port 5I:box 1:bay 1, SAS, 600 GB, OK)
Mirror Group 1:
physicaldrive 5I:1:2 (port 5I:box 1:bay 2, SAS, 600 GB, OK)
Drive Type: Data
LD Acceleration Method: Controller Cache
physicaldrive 5I:1:1
Port: 5I
Box: 1
Bay: 1
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD5
Serial Number: 6SL7G55D0000N4173JLT
Model: HP EF0600FARNA
Current Temperature (C): 37
Maximum Temperature (C): 43
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
physicaldrive 5I:1:2
Port: 5I
Box: 1
Bay: 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
Serial Number: 6SL7H2DM0000B41800Y0
Model: HP EF0600FARNA
Current Temperature (C): 37
Maximum Temperature (C): 44
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
'''
DRIVE_2_RAID_1_OKAY_TO_SHARE = '''
Available options are:
Max: 521094 (Units in MB)
Min: 16 (Units in MB)
'''
TWO_DRIVES_50GB_RAID1 = '''
Smart Array P822 in Slot 2
Bus Interface: PCI
Slot: 2
Serial Number: PDVTF0BRH5T0MO
Cache Serial Number: PBKUD0BRH5T3I6
RAID 6 (ADG) Status: Enabled
Controller Status: OK
Hardware Revision: B
Firmware Version: 4.68
Rebuild Priority: Medium
Expand Priority: Medium
Surface Scan Delay: 3 secs
Surface Scan Mode: Idle
Queue Depth: Automatic
Monitor and Performance Delay: 60 min
Elevator Sort: Enabled
Degraded Performance Optimization: Disabled
Inconsistency Repair Policy: Disabled
Wait for Cache Room: Disabled
Surface Analysis Inconsistency Notification: Disabled
Post Prompt Timeout: 15 secs
Cache Board Present: True
Cache Status: OK
Cache Ratio: 10% Read / 90% Write
Drive Write Cache: Disabled
Total Cache Size: 2.0 GB
Total Cache Memory Available: 1.8 GB
No-Battery Write Cache: Disabled
Cache Backup Power Source: Capacitors
Battery/Capacitor Count: 1
Battery/Capacitor Status: OK
SATA NCQ Supported: True
Spare Activation Mode: Activate on physical drive failure (default)
Controller Temperature (C): 88
Cache Module Temperature (C): 38
Capacitor Temperature (C): 23
Number of Ports: 6 (2 Internal / 4 External )
Driver Name: hpsa
Driver Version: 3.4.4
Driver Supports HP SSD Smart Path: True
Array: A
Interface Type: SAS
Unused Space: 939791 MB
Status: OK
MultiDomain Status: OK
Array Type: Data
HP SSD Smart Path: disable
Logical Drive: 1
Size: 50.0 GB
Fault Tolerance: 1
Heads: 255
Sectors Per Track: 32
Cylinders: 12850
Strip Size: 256 KB
Full Stripe Size: 256 KB
Status: OK
MultiDomain Status: OK
Caching: Enabled
Unique Identifier: 600508B1001C02BDBCB659B8A264186A
Disk Name: /dev/sda
Mount Points: None
Logical Drive Label: 02896A0EPDVTF0BRH5T0MOEBAA
Mirror Group 0:
physicaldrive 5I:1:1 (port 5I:box 1:bay 1, SAS, 600 GB, OK)
Mirror Group 1:
physicaldrive 5I:1:2 (port 5I:box 1:bay 2, SAS, 600 GB, OK)
Drive Type: Data
LD Acceleration Method: Controller Cache
Logical Drive: 2
Size: 50.0 GB
Fault Tolerance: 1
Heads: 255
Sectors Per Track: 32
Cylinders: 12850
Strip Size: 256 KB
Full Stripe Size: 256 KB
Status: OK
MultiDomain Status: OK
Caching: Enabled
Unique Identifier: 600508B1001C1614116817E8A9DA1D2F
Disk Name: /dev/sdb
Mount Points: None
Logical Drive Label: 06896EEAPDVTF0BRH5T0MO55C7
Mirror Group 0:
physicaldrive 5I:1:1 (port 5I:box 1:bay 1, SAS, 600 GB, OK)
Mirror Group 1:
physicaldrive 5I:1:2 (port 5I:box 1:bay 2, SAS, 600 GB, OK)
Drive Type: Data
LD Acceleration Method: Controller Cache
physicaldrive 5I:1:1
Port: 5I
Box: 1
Bay: 1
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
Serial Number: 6SL7G55D0000N4173JLT
Model: HP EF0600FARNA
Current Temperature (C): 37
Maximum Temperature (C): 43
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
physicaldrive 5I:1:2
Port: 5I
Box: 1
Bay: 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
Serial Number: 6SL7H2DM0000B41800Y0
Model: HP EF0600FARNA
Current Temperature (C): 37
Maximum Temperature (C): 44
PHY Count: 2
PHY Transfer Rate: 6.0Gbps, Unknown
Drive Authentication Status: OK
Carrier Application Version: 11
Carrier Bootloader Version: 6
SEP (Vendor ID PMCSIERA, Model SRCv24x6G) 380
Device Number: 380
Firmware Version: RevB
WWID: 5001438028842E1F
Vendor ID: PMCSIERA
Model: SRCv24x6G
'''

View File

@@ -107,7 +107,8 @@ class DiskAllocatorTestCase(testtools.TestCase):
disk1.size_gb = 300 disk1.size_gb = 300
disk2.size_gb = 300 disk2.size_gb = 300
disk_allocator.allocate_disks(logical_disk, server) raid_config = {'logical_disks': [logical_disk]}
disk_allocator.allocate_disks(logical_disk, server, raid_config)
self.assertEqual('Smart Array P822 in Slot 2', self.assertEqual('Smart Array P822 in Slot 2',
logical_disk['controller']) logical_disk['controller'])
self.assertEqual(sorted(['5I:1:3', '6I:1:7']), self.assertEqual(sorted(['5I:1:3', '6I:1:7']),
@@ -122,9 +123,10 @@ class DiskAllocatorTestCase(testtools.TestCase):
'raid_level': '1', 'raid_level': '1',
'disk_type': 'hdd', 'disk_type': 'hdd',
'interface_type': 'sas'} 'interface_type': 'sas'}
raid_config = {'logical_disks': [logical_disk]}
exc = self.assertRaises(exception.PhysicalDisksNotFoundError, exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks, disk_allocator.allocate_disks,
logical_disk, server) logical_disk, server, raid_config)
self.assertIn("of size 700 GB and raid level 1", str(exc)) self.assertIn("of size 700 GB and raid level 1", str(exc))
def test_allocate_disks_disk_not_enough_disks(self, def test_allocate_disks_disk_not_enough_disks(self,
@@ -139,7 +141,92 @@ class DiskAllocatorTestCase(testtools.TestCase):
'raid_level': '5', 'raid_level': '5',
'disk_type': 'hdd', 'disk_type': 'hdd',
'interface_type': 'sas'} 'interface_type': 'sas'}
raid_config = {'logical_disks': [logical_disk]}
exc = self.assertRaises(exception.PhysicalDisksNotFoundError, exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks, disk_allocator.allocate_disks,
logical_disk, server) logical_disk, server, raid_config)
self.assertIn("of size 600 GB and raid level 5", str(exc)) self.assertIn("of size 600 GB and raid level 5", str(exc))
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_allocate_disks_share_physical_disks(self, execute_mock,
get_all_details_mock):
get_all_details_mock.return_value = raid_constants.ONE_DRIVE_RAID_1
execute_mock.return_value = (
raid_constants.DRIVE_2_RAID_1_OKAY_TO_SHARE, None)
rdh = {'wwn': '0x600508b1001c02bd'}
controller = 'Smart Array P822 in Slot 2'
physical_disks = ['5I:1:1', '5I:1:2']
raid_config = {'logical_disks': [{'size_gb': 50,
'raid_level': '1',
'share_physical_disks': True,
'root_device_hint': rdh,
'controller': controller,
'physical_disks': physical_disks},
{'size_gb': 50,
'raid_level': '1',
'share_physical_disks': True}]}
logical_disk = raid_config['logical_disks'][1]
server = objects.Server()
disk_allocator.allocate_disks(logical_disk, server, raid_config)
self.assertEqual(controller, logical_disk['controller'])
self.assertEqual('A', logical_disk['array'])
self.assertNotIn('physical_disks', logical_disk)
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_allocate_disks_share_physical_disks_no_space(
self, execute_mock, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.ONE_DRIVE_RAID_1
execute_mock.return_value = (
raid_constants.DRIVE_2_RAID_1_OKAY_TO_SHARE, None)
rdh = {'wwn': '0x600508b1001c02bd'}
controller = 'Smart Array P822 in Slot 2'
physical_disks = ['5I:1:1', '5I:1:2']
raid_config = {'logical_disks': [{'size_gb': 50,
'raid_level': '1',
'share_physical_disks': True,
'root_device_hint': rdh,
'controller': controller,
'physical_disks': physical_disks},
{'size_gb': 600,
'raid_level': '1',
'share_physical_disks': True}]}
logical_disk = raid_config['logical_disks'][1]
server = objects.Server()
self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks,
logical_disk, server, raid_config)
def test_allocate_disks_share_physical_disks_criteria_mismatch(
self, get_all_details_mock):
# Both the drives don't have firmware HPD6
get_all_details_mock.return_value = raid_constants.ONE_DRIVE_RAID_1
rdh = {'wwn': '0x600508b1001c02bd'}
controller = 'Smart Array P822 in Slot 2'
physical_disks = ['5I:1:1', '5I:1:2']
raid_config = {'logical_disks': [{'size_gb': 50,
'raid_level': '1',
'share_physical_disks': True,
'root_device_hint': rdh,
'controller': controller,
'physical_disks': physical_disks},
{'size_gb': 50,
'raid_level': '1',
'firmware': 'HPD6',
'share_physical_disks': True}]}
logical_disk = raid_config['logical_disks'][1]
server = objects.Server()
self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks,
logical_disk, server, raid_config)

View File

@@ -163,6 +163,44 @@ class ManagerTestCases(testtools.TestCase):
raid_info) raid_info)
self.assertIn("of size 50 GB and raid level 1", str(exc)) self.assertIn("of size 50 GB and raid level 1", str(exc))
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_configuration_share_physical_disks(
self, controller_exec_cmd_mock, get_all_details_mock):
no_drives = raid_constants.HPSSA_NO_DRIVES_2_PHYSICAL_DISKS
one_drive = raid_constants.ONE_DRIVE_RAID_1
two_drives = raid_constants.TWO_DRIVES_50GB_RAID1
get_all_details_mock.side_effect = [no_drives, one_drive, two_drives]
controller_exec_cmd_mock.side_effect = [
(None, None),
(raid_constants.DRIVE_2_RAID_1_OKAY_TO_SHARE, None),
(None, None)]
raid_info = {'logical_disks': [{'size_gb': 50,
'share_physical_disks': True,
'raid_level': '1',
'disk_type': 'hdd'},
{'size_gb': 50,
'share_physical_disks': True,
'raid_level': '1',
'disk_type': 'hdd'}]}
raid_info = manager.create_configuration(raid_info)
ld1 = raid_info['logical_disks'][0]
ld2 = raid_info['logical_disks'][1]
self.assertEqual('Smart Array P822 in Slot 2', ld1['controller'])
self.assertEqual('Smart Array P822 in Slot 2', ld2['controller'])
self.assertEqual(sorted(['5I:1:1', '5I:1:2']),
sorted(ld1['physical_disks']))
self.assertEqual(sorted(['5I:1:1', '5I:1:2']),
sorted(ld2['physical_disks']))
controller_exec_cmd_mock.assert_any_call(
'create', 'type=logicaldrive', 'drives=5I:1:1,5I:1:2',
'raid=1', 'size=51200')
controller_exec_cmd_mock.assert_any_call(
'array', 'A', 'create', 'type=logicaldrive', 'raid=1', 'size=?',
dont_transform_to_hpssa_exception=True)
controller_exec_cmd_mock.assert_any_call(
'array', 'A', 'create', 'type=logicaldrive', 'raid=1',
'size=51200')
@mock.patch.object(objects.Controller, 'execute_cmd') @mock.patch.object(objects.Controller, 'execute_cmd')
def test_delete_configuration(self, controller_exec_cmd_mock, def test_delete_configuration(self, controller_exec_cmd_mock,
get_all_details_mock): get_all_details_mock):

View File

@@ -208,8 +208,8 @@ class ControllerTest(testtools.TestCase):
'foo', 'bar') 'foo', 'bar')
@mock.patch.object(objects.Controller, 'execute_cmd') @mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_logical_drive(self, execute_mock, def test_create_logical_drive_with_physical_disks(self, execute_mock,
get_all_details_mock): 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
@@ -225,16 +225,36 @@ class ControllerTest(testtools.TestCase):
'5I:1:2', '5I:1:2',
'5I:1:3']} '5I:1:3']}
controller.create_logical_drive(logical_drive_info, controller.create_logical_drive(logical_drive_info)
['5I:1:1',
'5I:1:2',
'5I:1:3'])
execute_mock.assert_called_once_with("create", execute_mock.assert_called_once_with("create",
"type=logicaldrive", "type=logicaldrive",
"drives=5I:1:1,5I:1:2,5I:1:3", "drives=5I:1:1,5I:1:2,5I:1:3",
"raid=1", "raid=1",
"size=51200") "size=51200")
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_logical_drive_with_raid_array(self, execute_mock,
get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES
server = objects.Server()
controller = server.controllers[0]
logical_drive_info = {'size_gb': 50,
'raid_level': '1',
'volume_name': 'boot_volume',
'is_boot_volume': 'true',
'controller': 'Smart Array P822 in Slot 2',
'array': 'A'}
controller.create_logical_drive(logical_drive_info)
execute_mock.assert_called_once_with("array", "A",
"create",
"type=logicaldrive",
"raid=1",
"size=51200")
@mock.patch.object(objects.Controller, 'execute_cmd') @mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_logical_drive_raid_level_mapping(self, execute_mock, def test_create_logical_drive_raid_level_mapping(self, execute_mock,
get_all_details_mock): get_all_details_mock):
@@ -256,9 +276,7 @@ class ControllerTest(testtools.TestCase):
'5I:1:5', '5I:1:5',
'6I:1:6']} '6I:1:6']}
controller.create_logical_drive(logical_drive_info, controller.create_logical_drive(logical_drive_info)
['5I:1:1', '5I:1:2', '5I:1:3',
'5I:1:4', '5I:1:5', '6I:1:6'])
execute_mock.assert_called_once_with( execute_mock.assert_called_once_with(
"create", "type=logicaldrive", "create", "type=logicaldrive",
"drives=5I:1:1,5I:1:2,5I:1:3,5I:1:4,5I:1:5,6I:1:6", "drives=5I:1:1,5I:1:2,5I:1:3,5I:1:4,5I:1:5,6I:1:6",
@@ -329,6 +347,95 @@ class LogicalDriveTest(testtools.TestCase):
self.assertIn(msg, str(ex)) self.assertIn(msg, str(ex))
@mock.patch.object(objects.Server, '_get_all_details')
class ArrayTest(testtools.TestCase):
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_okay(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.return_value = current_config
execute_mock.return_value = (
raid_constants.ARRAY_ACCOMODATE_LOGICAL_DISK, None)
logical_disk = {'size_gb': 500, 'raid_level': '5'}
server = objects.Server()
ret_val = server.controllers[0].raid_arrays[0].can_accomodate(
logical_disk)
self.assertTrue(ret_val)
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_not_enough_space(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.return_value = current_config
execute_mock.return_value = (
raid_constants.ARRAY_ACCOMODATE_LOGICAL_DISK, None)
logical_disk = {'size_gb': 1500, 'raid_level': '5'}
server = objects.Server()
ret_val = server.controllers[0].raid_arrays[0].can_accomodate(
logical_disk)
self.assertFalse(ret_val)
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_invalid_raid_level(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.return_value = current_config
exc = processutils.ProcessExecutionError(
stdout=raid_constants.ARRAY_ACCOMODATE_LOGICAL_DISK_INVALID,
stderr=None,
exit_code=1)
execute_mock.side_effect = exc
logical_disk = {'size_gb': 1500, 'raid_level': '1'}
server = objects.Server()
ret_val = server.controllers[0].raid_arrays[0].can_accomodate(
logical_disk)
self.assertFalse(ret_val)
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_some_other_error(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.return_value = current_config
exc = processutils.ProcessExecutionError(
stdout=raid_constants.ARRAY_ACCOMODATE_LOGICAL_DISK_INVALID,
stderr=None,
exit_code=2)
execute_mock.side_effect = exc
logical_disk = {'size_gb': 1500, 'raid_level': '1'}
server = objects.Server()
self.assertRaises(
exception.HPSSAOperationError,
server.controllers[0].raid_arrays[0].can_accomodate,
logical_disk)
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_oserror(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.return_value = current_config
execute_mock.side_effect = OSError
logical_disk = {'size_gb': 1500, 'raid_level': '1'}
server = objects.Server()
self.assertRaises(
exception.HPSSAOperationError,
server.controllers[0].raid_arrays[0].can_accomodate,
logical_disk)
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_map_raid_level(self, execute_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
execute_mock.return_value = ("", None)
get_all_details_mock.return_value = current_config
logical_disk = {'size_gb': 1500, 'raid_level': '5+0'}
server = objects.Server()
server.controllers[0].raid_arrays[0].can_accomodate(logical_disk)
execute_mock.assert_called_once_with(
"hpssacli", "controller", "slot=2", "array", mock.ANY, "create",
"type=logicaldrive", "raid=50", "size=?")
@mock.patch.object(objects.Server, '_get_all_details') @mock.patch.object(objects.Server, '_get_all_details')
class PhysicalDriveTest(testtools.TestCase): class PhysicalDriveTest(testtools.TestCase):
@@ -383,3 +490,30 @@ 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'])
class PrivateMethodsTestCase(testtools.TestCase):
@mock.patch.object(processutils, 'execute')
def test__hpssacli(self, execute_mock):
execute_mock.return_value = ("stdout", "stderr")
stdout, stderr = objects._hpssacli("foo", "bar",
check_exit_code=[0, 1, 2, 3])
execute_mock.assert_called_once_with(
"hpssacli", "foo", "bar", check_exit_code=[0, 1, 2, 3])
self.assertEqual("stdout", stdout)
self.assertEqual("stderr", stderr)
@mock.patch.object(processutils, 'execute')
def test__hpssacli_raises_error(self, execute_mock):
execute_mock.side_effect = OSError
self.assertRaises(exception.HPSSAOperationError,
objects._hpssacli, "foo", "bar")
@mock.patch.object(processutils, 'execute')
def test__hpssacli_raises_error_no_transform(self, execute_mock):
execute_mock.side_effect = OSError
self.assertRaises(OSError,
objects._hpssacli, "foo", "bar",
dont_transform_to_hpssa_exception=True)
execute_mock.assert_called_once_with("hpssacli", "foo", "bar")