From 615ac0eee2bccfe50bd73671b900de5b29664c1b Mon Sep 17 00:00:00 2001 From: Aparna Date: Thu, 10 Nov 2016 11:32:58 +0000 Subject: [PATCH] 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 --- proliantutils/exception.py | 4 +- proliantutils/hpssa/manager.py | 2 +- proliantutils/hpssa/objects.py | 64 +++++++++++++---------- proliantutils/tests/hpssa/test_manager.py | 10 ++-- proliantutils/tests/hpssa/test_objects.py | 58 ++++++++++++++------ 5 files changed, 84 insertions(+), 54 deletions(-) diff --git a/proliantutils/exception.py b/proliantutils/exception.py index c4f4179..c9ff51f 100644 --- a/proliantutils/exception.py +++ b/proliantutils/exception.py @@ -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.") diff --git a/proliantutils/hpssa/manager.py b/proliantutils/hpssa/manager.py index 17fb242..bad4314 100644 --- a/proliantutils/hpssa/manager.py +++ b/proliantutils/hpssa/manager.py @@ -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() diff --git a/proliantutils/hpssa/objects.py b/proliantutils/hpssa/objects.py index c51215a..b64b850 100644 --- a/proliantutils/hpssa/objects.py +++ b/proliantutils/hpssa/objects.py @@ -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, diff --git a/proliantutils/tests/hpssa/test_manager.py b/proliantutils/tests/hpssa/test_manager.py index 7cd8075..41da928 100644 --- a/proliantutils/tests/hpssa/test_manager.py +++ b/proliantutils/tests/hpssa/test_manager.py @@ -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') diff --git a/proliantutils/tests/hpssa/test_objects.py b/proliantutils/tests/hpssa/test_objects.py index e98ec9c..cfdcde7 100644 --- a/proliantutils/tests/hpssa/test_objects.py +++ b/proliantutils/tests/hpssa/test_objects.py @@ -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)