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)