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:
@@ -32,19 +32,19 @@ def _get_criteria_matching_disks(logical_disk, physical_drives):
|
||||
criteria_to_consider = [x for x in FILTER_CRITERIA
|
||||
if x in logical_disk]
|
||||
|
||||
for physical_drive in physical_drives:
|
||||
for physical_drive_object in physical_drives:
|
||||
for criteria in criteria_to_consider:
|
||||
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:
|
||||
break
|
||||
else:
|
||||
matching_physical_drives.append(physical_drive)
|
||||
matching_physical_drives.append(physical_drive_object)
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
from the RAID configuration input to the module.
|
||||
:param server: An objects.Server object
|
||||
:param raid_config: The target RAID configuration requested.
|
||||
:raises: PhysicalDisksNotFoundError, if cannot find
|
||||
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])
|
||||
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:
|
||||
physical_drives = controller.unassigned_physical_drives
|
||||
physical_drives = _get_criteria_matching_disks(logical_disk,
|
||||
@@ -76,13 +78,39 @@ def allocate_disks(logical_disk, server):
|
||||
logical_disk['controller'] = controller.id
|
||||
physical_disks = selected_drive_ids[:number_of_physical_disks]
|
||||
logical_disk['physical_disks'] = physical_disks
|
||||
break
|
||||
return
|
||||
|
||||
if not share_physical_disks:
|
||||
# TODO(rameshg87): When this logical drives can share disks
|
||||
# with other arrays, figure out free space in other arrays
|
||||
# and then consider which array to use.
|
||||
pass
|
||||
else:
|
||||
raise exception.PhysicalDisksNotFoundError(size_gb=size_gb,
|
||||
raid_level=raid_level)
|
||||
# We didn't find physical disks to create an independent array.
|
||||
# Check if we can get some shared arrays.
|
||||
if share_physical_disks:
|
||||
sharable_disk_wwns = []
|
||||
for sharable_logical_disk in raid_config['logical_disks']:
|
||||
if (sharable_logical_disk.get('share_physical_disks', False) and
|
||||
'root_device_hint' in sharable_logical_disk):
|
||||
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)
|
||||
|
||||
@@ -116,7 +116,8 @@ def create_configuration(raid_config):
|
||||
for logical_disk in logical_disks_sorted:
|
||||
|
||||
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']
|
||||
|
||||
@@ -125,18 +126,17 @@ def create_configuration(raid_config):
|
||||
msg = ("Unable to find controller named '%s'" % controller_id)
|
||||
raise exception.InvalidInputError(reason=msg)
|
||||
|
||||
for physical_disk in logical_disk['physical_disks']:
|
||||
disk_obj = controller.get_physical_drive_by_id(physical_disk)
|
||||
if not disk_obj:
|
||||
msg = ("Unable to find physical disk '%(physical_disk)s' "
|
||||
"on '%(controller)s'" %
|
||||
{'physical_disk': physical_disk,
|
||||
'controller': controller_id})
|
||||
raise exception.InvalidInputError(msg)
|
||||
if 'physical_disks' in logical_disk:
|
||||
for physical_disk in logical_disk['physical_disks']:
|
||||
disk_obj = controller.get_physical_drive_by_id(physical_disk)
|
||||
if not disk_obj:
|
||||
msg = ("Unable to find physical disk '%(physical_disk)s' "
|
||||
"on '%(controller)s'" %
|
||||
{'physical_disk': physical_disk,
|
||||
'controller': controller_id})
|
||||
raise exception.InvalidInputError(msg)
|
||||
|
||||
physical_drive_ids = logical_disk['physical_disks']
|
||||
|
||||
controller.create_logical_drive(logical_disk, physical_drive_ids)
|
||||
controller.create_logical_drive(logical_disk)
|
||||
|
||||
# Now find the new logical drive created.
|
||||
server.refresh()
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
from oslo.concurrency import processutils
|
||||
@@ -115,13 +116,36 @@ def _convert_to_dict(stdout):
|
||||
return info_dict
|
||||
|
||||
|
||||
def _hpssacli(*args):
|
||||
"""Wrapper function for executing hpssacli command."""
|
||||
def _hpssacli(*args, **kwargs):
|
||||
"""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:
|
||||
stdout, stderr = processutils.execute("hpssacli",
|
||||
*args)
|
||||
*args, **kwargs)
|
||||
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
|
||||
|
||||
@@ -318,22 +342,23 @@ class Controller(object):
|
||||
return phy_drive
|
||||
return None
|
||||
|
||||
def execute_cmd(self, *args):
|
||||
def execute_cmd(self, *args, **kwargs):
|
||||
"""Execute a given hpssacli command on the controller.
|
||||
|
||||
This method executes a given command on the controller.
|
||||
|
||||
:params args: a tuple consisting of sub-commands to be appended
|
||||
after specifying the controller in hpssacli command.
|
||||
:param kwargs: kwargs to be passed to execute() in processutils
|
||||
:raises: HPSSAOperationError, if hpssacli operation failed.
|
||||
"""
|
||||
|
||||
slot = self.properties['Slot']
|
||||
base_cmd = ("controller", "slot=%s" % slot)
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
"""
|
||||
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
|
||||
raid_level = logical_drive_info['raid_level']
|
||||
|
||||
@@ -352,10 +385,9 @@ class Controller(object):
|
||||
# Check if we have mapping stored, otherwise use the same.
|
||||
raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get(
|
||||
raid_level, raid_level)
|
||||
self.execute_cmd("create", "type=logicaldrive",
|
||||
"drives=%s" % phy_drive_ids,
|
||||
"raid=%s" % raid_level,
|
||||
"size=%s" % size_mb)
|
||||
cmd_args.extend(["raid=%s" % raid_level, "size=%s" % size_mb])
|
||||
|
||||
self.execute_cmd(*cmd_args)
|
||||
|
||||
def delete_all_logical_drives(self):
|
||||
"""Deletes all logical drives on trh controller.
|
||||
@@ -395,6 +427,48 @@ class RaidArray(object):
|
||||
properties[physical_drive],
|
||||
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 for LogicalDrive object."""
|
||||
|
||||
@@ -1411,3 +1411,383 @@ Smart Array P822 in Slot 2
|
||||
Carrier Application Version: 11
|
||||
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
|
||||
'''
|
||||
|
||||
@@ -107,7 +107,8 @@ class DiskAllocatorTestCase(testtools.TestCase):
|
||||
disk1.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',
|
||||
logical_disk['controller'])
|
||||
self.assertEqual(sorted(['5I:1:3', '6I:1:7']),
|
||||
@@ -122,9 +123,10 @@ class DiskAllocatorTestCase(testtools.TestCase):
|
||||
'raid_level': '1',
|
||||
'disk_type': 'hdd',
|
||||
'interface_type': 'sas'}
|
||||
raid_config = {'logical_disks': [logical_disk]}
|
||||
exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
|
||||
disk_allocator.allocate_disks,
|
||||
logical_disk, server)
|
||||
logical_disk, server, raid_config)
|
||||
self.assertIn("of size 700 GB and raid level 1", str(exc))
|
||||
|
||||
def test_allocate_disks_disk_not_enough_disks(self,
|
||||
@@ -139,7 +141,92 @@ class DiskAllocatorTestCase(testtools.TestCase):
|
||||
'raid_level': '5',
|
||||
'disk_type': 'hdd',
|
||||
'interface_type': 'sas'}
|
||||
raid_config = {'logical_disks': [logical_disk]}
|
||||
exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
|
||||
disk_allocator.allocate_disks,
|
||||
logical_disk, server)
|
||||
logical_disk, server, raid_config)
|
||||
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)
|
||||
|
||||
@@ -163,6 +163,44 @@ class ManagerTestCases(testtools.TestCase):
|
||||
raid_info)
|
||||
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')
|
||||
def test_delete_configuration(self, controller_exec_cmd_mock,
|
||||
get_all_details_mock):
|
||||
|
||||
@@ -208,8 +208,8 @@ class ControllerTest(testtools.TestCase):
|
||||
'foo', 'bar')
|
||||
|
||||
@mock.patch.object(objects.Controller, 'execute_cmd')
|
||||
def test_create_logical_drive(self, execute_mock,
|
||||
get_all_details_mock):
|
||||
def test_create_logical_drive_with_physical_disks(self, execute_mock,
|
||||
get_all_details_mock):
|
||||
|
||||
get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES
|
||||
|
||||
@@ -225,16 +225,36 @@ class ControllerTest(testtools.TestCase):
|
||||
'5I:1:2',
|
||||
'5I:1:3']}
|
||||
|
||||
controller.create_logical_drive(logical_drive_info,
|
||||
['5I:1:1',
|
||||
'5I:1:2',
|
||||
'5I:1:3'])
|
||||
controller.create_logical_drive(logical_drive_info)
|
||||
execute_mock.assert_called_once_with("create",
|
||||
"type=logicaldrive",
|
||||
"drives=5I:1:1,5I:1:2,5I:1:3",
|
||||
"raid=1",
|
||||
"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')
|
||||
def test_create_logical_drive_raid_level_mapping(self, execute_mock,
|
||||
get_all_details_mock):
|
||||
@@ -256,9 +276,7 @@ class ControllerTest(testtools.TestCase):
|
||||
'5I:1:5',
|
||||
'6I:1:6']}
|
||||
|
||||
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'])
|
||||
controller.create_logical_drive(logical_drive_info)
|
||||
execute_mock.assert_called_once_with(
|
||||
"create", "type=logicaldrive",
|
||||
"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))
|
||||
|
||||
|
||||
@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')
|
||||
class PhysicalDriveTest(testtools.TestCase):
|
||||
|
||||
@@ -383,3 +490,30 @@ class PhysicalDriveTest(testtools.TestCase):
|
||||
self.assertEqual('HP EF0600FARNA', ret['model'])
|
||||
self.assertEqual('HPD6', ret['firmware'])
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user