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
|
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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|||||||
@@ -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
|
||||||
|
'''
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user