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