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

@@ -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."""