Update hpssacli to ssacli

Updating the older hpssacli with the new
ssacli command.

This change continue to support hpssacli
by falling back when ssacli is not available
allowing the user to update the environment.

Change-Id: Ia763eff898dff2836fd4f1bc1889ae6f44206261
This commit is contained in:
Aparna
2016-11-10 11:32:58 +00:00
parent 6ca31c0c9d
commit 615ac0eee2
5 changed files with 84 additions and 54 deletions

View File

@@ -122,7 +122,7 @@ class IloInvalidInputError(IloError):
class HPSSAException(ProliantUtilsException):
message = "An exception occured in hpssa module"
message = "An exception occured in ssa module"
def __init__(self, message=None, **kwargs):
if not message:
@@ -140,7 +140,7 @@ class PhysicalDisksNotFoundError(HPSSAException):
class HPSSAOperationError(HPSSAException):
message = ("An error was encountered while doing hpssa configuration: "
message = ("An error was encountered while doing ssa configuration: "
"%(reason)s.")

View File

@@ -311,7 +311,7 @@ def delete_configuration():
for controller in server.controllers:
# Trigger delete only if there is some RAID array, otherwise
# hpssacli will fail saying "no logical drives found."
# hpssacli/ssacli will fail saying "no logical drives found.".
if controller.raid_arrays:
controller.delete_all_logical_drives()
return get_configuration()

View File

@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import re
import time
@@ -56,7 +57,7 @@ def _get_key_value(string):
def _get_dict(lines, start_index, indentation):
"""Recursive function for parsing hpssacli output."""
"""Recursive function for parsing hpssacli/ssacli output."""
info = {}
current_item = None
@@ -103,9 +104,9 @@ def _get_dict(lines, start_index, indentation):
def _convert_to_dict(stdout):
"""Wrapper function for parsing hpssacli command.
"""Wrapper function for parsing hpssacli/ssacli command.
This function gets the output from hpssacli command
This function gets the output from hpssacli/ssacli command
and calls the recursive function _get_dict to return
the complete dictionary containing the RAID information.
"""
@@ -116,16 +117,18 @@ def _convert_to_dict(stdout):
return info_dict
def _hpssacli(*args, **kwargs):
"""Wrapper function for executing hpssacli command.
def _ssacli(*args, **kwargs):
"""Wrapper function for executing hpssacli/ssacli command.
:param args: args to be provided to hpssacli command
This function executes ssacli command if it exists, else it
falls back to hpssacli.
:param args: args to be provided to hpssacli/ssacli 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 only when hpssa controller is available. This is
useful when the return code from hpssacli is useful for
useful when the return code from hpssacli/ssacli is useful for
analysis.
:returns: a tuple containing the stdout and stderr after running
the process.
@@ -140,11 +143,15 @@ def _hpssacli(*args, **kwargs):
kwargs.pop('dont_transform_to_hpssa_exception', None)
try:
stdout, stderr = processutils.execute("hpssacli",
*args, **kwargs)
if os.path.exists("/usr/sbin/ssacli"):
stdout, stderr = processutils.execute("ssacli",
*args, **kwargs)
else:
stdout, stderr = processutils.execute("hpssacli",
*args, **kwargs)
except (OSError, processutils.ProcessExecutionError) as e:
if 'No controllers detected' in str(e):
msg = ("HPSSA controller not found. Enable hpssa controller"
msg = ("SSA controller not found. Enable ssa controller"
" to continue with the desired operation")
raise exception.HPSSAOperationError(reason=msg)
elif not dont_transform_to_hpssa_exception:
@@ -172,10 +179,10 @@ class Server(object):
"""Gets the current RAID configuration on the server.
This methods gets the current RAID configuration on the server using
hpssacli command and returns the output.
hpssacli/ssacli command and returns the output.
:returns: stdout after running the hpssacli command. The output looks
as follows:
:returns: stdout after running the hpssacli/ssacli command. The output
looks as follows:
Smart Array P822 in Slot 2
Bus Interface: PCI
@@ -216,10 +223,10 @@ class Server(object):
Interface Type: SAS
Drive Type: Data Drive
:raises: HPSSAOperationError, if hpssacli operation failed.
:raises: HPSSAOperationError, if hpssacli/ssacli operation failed.
"""
stdout, stderr = _hpssacli("controller", "all", "show",
"config", "detail")
stdout, stderr = _ssacli("controller", "all", "show",
"config", "detail")
return stdout
def refresh(self):
@@ -227,9 +234,9 @@ class Server(object):
This method removes all the cache information in the server
and it's child objects, and fetches the information again from
the server using hpssacli command.
the server using hpssacli/ssacli command.
:raises: HPSSAOperationError, if hpssacli operation failed.
:raises: HPSSAOperationError, if hpssacli/ssacli operation failed.
"""
config = self._get_all_details()
@@ -353,20 +360,19 @@ class Controller(object):
return None
def execute_cmd(self, *args, **kwargs):
"""Execute a given hpssacli command on the controller.
"""Execute a given hpssacli/ssacli 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.
after specifying the controller in hpssacli/ssacli command.
:param kwargs: kwargs to be passed to execute() in processutils
:raises: HPSSAOperationError, if hpssacli operation failed.
:raises: HPSSAOperationError, if hpssacli/ssacli operation failed.
"""
slot = self.properties['Slot']
base_cmd = ("controller", "slot=%s" % slot)
cmd = base_cmd + args
return _hpssacli(*cmd, **kwargs)
return _ssacli(*cmd, **kwargs)
def create_logical_drive(self, logical_drive_info):
"""Create a logical drive on the controller.
@@ -376,7 +382,7 @@ class Controller(object):
:param logical_drive_info: a dictionary containing the details
of the logical drive as specified in raid config.
:raises: HPSSAOperationError, if hpssacli operation failed.
:raises: HPSSAOperationError, if hpssacli/ssacli operation failed.
"""
cmd_args = []
if 'array' in logical_drive_info:
@@ -408,7 +414,7 @@ class Controller(object):
"""Deletes all logical drives on trh controller.
This method deletes all logical drives on trh controller.
:raises: HPSSAOperationError, if hpssacli operation failed.
:raises: HPSSAOperationError, if hpssacli/ssacli operation failed.
"""
self.execute_cmd("logicaldrive", "all", "delete", "forced")
@@ -454,7 +460,7 @@ class RaidArray(object):
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
This method uses hpssacli/ssacli command's option to check if the
logical disk with desired size and RAID level can be created
on this RAID array.
@@ -476,7 +482,7 @@ class RaidArray(object):
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
# hpssacli/ssacli 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.
@@ -521,7 +527,7 @@ class LogicalDrive(object):
return_int=True) /
(1024*1024*1024)) - 1
except ValueError:
msg = ("hpssacli returned unknown size '%(size)s' for logical "
msg = ("ssacli returned unknown size '%(size)s' for logical "
"disk '%(logical_disk)s' of RAID array '%(array)s' in "
"controller '%(controller)s'." %
{'size': size, 'logical_disk': self.id,
@@ -582,7 +588,7 @@ class PhysicalDrive:
return_int=True) /
(1024*1024*1024))
except ValueError:
msg = ("hpssacli returned unknown size '%(size)s' for physical "
msg = ("ssacli returned unknown size '%(size)s' for physical "
"disk '%(physical_disk)s' of controller "
"'%(controller)s'." %
{'size': size, 'physical_disk': self.id,

View File

@@ -190,7 +190,7 @@ class ManagerTestCases(testtools.TestCase):
raid_info = {'logical_disks': 'foo'}
msg = ("An error was encountered while doing hpssa configuration: None"
msg = ("An error was encountered while doing ssa configuration: None"
" of the available SSA controllers Smart Array P822 in "
"Slot 3 have RAID enabled")
ex = self.assertRaises(exception.HPSSAOperationError,
@@ -404,7 +404,7 @@ class ManagerTestCases(testtools.TestCase):
drives = raid_constants.HPSSA_HBA_MODE
get_all_details_mock.return_value = drives
msg = ("An error was encountered while doing hpssa configuration: None"
msg = ("An error was encountered while doing ssa configuration: None"
" of the available SSA controllers Smart Array P822 in "
"Slot 3 have RAID enabled")
ex = self.assertRaises(exception.HPSSAOperationError,
@@ -460,9 +460,9 @@ class ManagerTestCases(testtools.TestCase):
select_controllers = lambda x: not x.properties.get('HBA Mode Enabled',
False)
msg = ("An error was encountered while doing hpssa configuration: "
"None of the available SSA controllers Smart Array P822 in "
"Slot 3 have Raid enabled.")
msg = ("An error was encountered while doing ssa configuration: "
"None of the available SSA controllers Smart Array P822 "
"in Slot 3 have Raid enabled.")
ex = self.assertRaises(exception.HPSSAOperationError,
manager._select_controllers_by,
server, select_controllers, 'Raid enabled')

View File

@@ -174,9 +174,11 @@ class ServerTest(testtools.TestCase):
@mock.patch.object(objects.Server, '_get_all_details')
class ControllerTest(testtools.TestCase):
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test_execute_cmd(self, processutils_mock, get_all_details_mock):
def test_execute_cmd(self, processutils_mock, path_mock,
get_all_details_mock):
path_mock.return_value = True
get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES
server = objects.Server()
@@ -186,7 +188,7 @@ class ControllerTest(testtools.TestCase):
stdout, stderr = controller.execute_cmd('foo', 'bar')
processutils_mock.assert_called_once_with("hpssacli",
processutils_mock.assert_called_once_with("ssacli",
"controller",
"slot=2",
"foo",
@@ -469,17 +471,19 @@ class ArrayTest(testtools.TestCase):
server.controllers[0].raid_arrays[0].can_accomodate,
logical_disk)
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test_can_accomodate_map_raid_level(self, execute_mock,
def test_can_accomodate_map_raid_level(self, execute_mock, path_mock,
get_all_details_mock):
current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
path_mock.return_value = True
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",
"ssacli", "controller", "slot=2", "array", mock.ANY, "create",
"type=logicaldrive", "raid=50", "size=?")
@@ -569,32 +573,40 @@ class PhysicalDriveTest(testtools.TestCase):
class PrivateMethodsTestCase(testtools.TestCase):
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test__hpssacli(self, execute_mock):
def test__ssacli(self, execute_mock, path_mock):
execute_mock.return_value = ("stdout", "stderr")
stdout, stderr = objects._hpssacli("foo", "bar",
check_exit_code=[0, 1, 2, 3])
path_mock.return_value = True
stdout, stderr = objects._ssacli("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])
"ssacli", "foo", "bar", check_exit_code=[0, 1, 2, 3])
self.assertEqual("stdout", stdout)
self.assertEqual("stderr", stderr)
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test__hpssacli_raises_error(self, execute_mock):
def test__ssacli_raises_error(self, execute_mock, path_mock):
path_mock.return_value = True
execute_mock.side_effect = OSError
self.assertRaises(exception.HPSSAOperationError,
objects._hpssacli, "foo", "bar")
objects._ssacli, "foo", "bar")
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test__hpssacli_raises_error_no_transform(self, execute_mock):
def test__ssacli_raises_error_no_transform(self, execute_mock, path_mock):
path_mock.return_value = True
execute_mock.side_effect = OSError
self.assertRaises(OSError,
objects._hpssacli, "foo", "bar",
objects._ssacli, "foo", "bar",
dont_transform_to_hpssa_exception=True)
execute_mock.assert_called_once_with("hpssacli", "foo", "bar")
execute_mock.assert_called_once_with("ssacli", "foo", "bar")
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test__hpssacli_raises_error_no_controller(self, execute_mock):
def test__ssacli_raises_error_no_controller(self, execute_mock, path_mock):
path_mock.return_value = True
value = ("Error: No controllers detected. Possible causes:"
" The driver for the installed controller(s) is not loaded."
" On LINUX, the scsi_generic (sg) driver module is not"
@@ -602,7 +614,19 @@ class PrivateMethodsTestCase(testtools.TestCase):
execute_mock.side_effect = processutils.ProcessExecutionError(
value)
ex = self.assertRaises(exception.HPSSAOperationError,
objects._hpssacli, "foo", "bar")
msg = ("HPSSA controller not found. Enable hpssa controller"
objects._ssacli, "foo", "bar")
msg = ("SSA controller not found. Enable ssa controller"
" to continue with the desired operation")
self.assertIn(msg, str(ex))
@mock.patch('os.path.exists')
@mock.patch.object(processutils, 'execute')
def test__hpssacli_exists(self, execute_mock, path_mock):
execute_mock.return_value = ("stdout", "stderr")
path_mock.return_value = False
stdout, stderr = objects._ssacli("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)