From ff214c1fd38c794617317ba396388557d7b93449 Mon Sep 17 00:00:00 2001 From: Anusha Ramineni Date: Fri, 20 Mar 2015 18:56:19 +0530 Subject: [PATCH] ILO:Check if iLO is up after reset_ilo This commit adds a function to check if the iLO link is up again after resetting the iLO. Change-Id: I70901f35097639dab20edce65cd1fc1dfe68fa43 --- proliantutils/ilo/common.py | 39 ++++++++++++++++++ proliantutils/ilo/ribcl.py | 4 ++ proliantutils/ilo/ris.py | 14 +++++++ proliantutils/tests/ilo/test_common.py | 55 ++++++++++++++++++++++++++ proliantutils/tests/ilo/test_ribcl.py | 5 ++- 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 proliantutils/ilo/common.py create mode 100644 proliantutils/tests/ilo/test_common.py diff --git a/proliantutils/ilo/common.py b/proliantutils/ilo/common.py new file mode 100644 index 0000000..3aaf403 --- /dev/null +++ b/proliantutils/ilo/common.py @@ -0,0 +1,39 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Common functionalities used by both RIBCL and RIS.""" + +import time + +from proliantutils import exception + +# Max number of times an operation to be retried +RETRY_COUNT = 2 + + +def wait_for_ilo_after_reset(ilo_object): + """Checks if iLO is up after reset.""" + + retry_count = RETRY_COUNT + while retry_count: + try: + # Delay for 5 sec, for the reset operation to take effect. + time.sleep(5) + ilo_object.get_product_name() + break + except exception.IloError: + retry_count -= 1 + else: + msg = ('iLO is not up after reset.') + raise exception.IloConnectionError(msg) diff --git a/proliantutils/ilo/ribcl.py b/proliantutils/ilo/ribcl.py index 6f4caaf..e7a3a8b 100644 --- a/proliantutils/ilo/ribcl.py +++ b/proliantutils/ilo/ribcl.py @@ -24,6 +24,7 @@ import xml.etree.ElementTree as etree import six from proliantutils import exception +from proliantutils.ilo import common from proliantutils.ilo import operations @@ -628,8 +629,11 @@ class RIBCLOperations(operations.IloOperations): """Resets the iLO. :raises: IloError, on an error from iLO. + :raises: IloConnectionError, if iLO is not up after reset. """ self._execute_command('RESET_RIB', 'RIB_INFO', 'write') + # Check if iLO is up again after reset. + common.wait_for_ilo_after_reset(self) def reset_ilo_credential(self, password): """Resets the iLO password. diff --git a/proliantutils/ilo/ris.py b/proliantutils/ilo/ris.py index cbc531b..c54b75b 100644 --- a/proliantutils/ilo/ris.py +++ b/proliantutils/ilo/ris.py @@ -23,6 +23,7 @@ import StringIO import urlparse from proliantutils import exception +from proliantutils.ilo import common from proliantutils.ilo import operations """ Currently this class supports only secure boot and firmware settings @@ -393,6 +394,15 @@ class RISOperations(operations.IloOperations): val = val.rstrip() if val.endswith(" ") else val+" " self._change_bios_setting({'CustomPostMessage': val}) + def get_product_name(self): + """Gets the product name of the server. + + :returns: server model name. + :raises: IloError, on an error from iLO. + """ + system = self._get_host_details() + return system['Model'] + def get_secure_boot_mode(self): """Get the status of secure boot. @@ -569,6 +579,7 @@ class RISOperations(operations.IloOperations): """Resets the iLO. :raises: IloError, on an error from iLO. + :raises: IloConnectionError, if iLO is not up after reset. :raises: IloCommandNotSupportedError, if the command is not supported on the server. """ @@ -594,6 +605,9 @@ class RISOperations(operations.IloOperations): msg = self._get_extended_error(response) raise exception.IloError(msg) + # Check if the iLO is up again. + common.wait_for_ilo_after_reset(self) + def reset_bios_to_default(self): """Resets the BIOS settings to default values. diff --git a/proliantutils/tests/ilo/test_common.py b/proliantutils/tests/ilo/test_common.py new file mode 100644 index 0000000..f2dcee4 --- /dev/null +++ b/proliantutils/tests/ilo/test_common.py @@ -0,0 +1,55 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +"""Test Class for Common Operations.""" +import unittest + +import mock + +from proliantutils import exception +from proliantutils.ilo import common +from proliantutils.ilo import ribcl +from proliantutils.tests.ilo import ribcl_sample_outputs as ribcl_output + + +class IloCommonModuleTestCase(unittest.TestCase): + + def setUp(self): + super(IloCommonModuleTestCase, self).setUp() + self.ribcl = ribcl.RIBCLOperations("x.x.x.x", "admin", "Admin", + 60, 443) + + @mock.patch.object(ribcl.RIBCLOperations, 'get_product_name') + def test_wait_for_ilo_after_reset_ribcl_ok(self, name_mock): + name_mock.return_value = ribcl_output.GET_PRODUCT_NAME + common.wait_for_ilo_after_reset(self.ribcl) + name_mock.assert_called_once_with() + + @mock.patch.object(ribcl.RIBCLOperations, 'get_product_name') + def test__check_link_status_retry(self, name_mock): + exc = exception.IloError('error') + name_mock.side_effect = [exc, ribcl_output.GET_PRODUCT_NAME] + common.wait_for_ilo_after_reset(self.ribcl) + self.assertEqual(common.RETRY_COUNT, name_mock.call_count) + name_mock.assert_called_with() + + @mock.patch.object(ribcl.RIBCLOperations, 'get_product_name') + def test__check_link_status_fail(self, name_mock): + exc = exception.IloError('error') + name_mock.side_effect = [exc, exc] + self.assertRaises(exception.IloConnectionError, + common.wait_for_ilo_after_reset, + self.ribcl) + self.assertEqual(common.RETRY_COUNT, name_mock.call_count) + name_mock.assert_called_with() diff --git a/proliantutils/tests/ilo/test_ribcl.py b/proliantutils/tests/ilo/test_ribcl.py index 58400b8..a387296 100644 --- a/proliantutils/tests/ilo/test_ribcl.py +++ b/proliantutils/tests/ilo/test_ribcl.py @@ -21,6 +21,7 @@ import mock import ribcl_sample_outputs as constants from proliantutils import exception +from proliantutils.ilo import common from proliantutils.ilo import ribcl @@ -166,11 +167,13 @@ class IloRibclTestCase(unittest.TestCase): except exception.IloCommandNotSupportedError as e: self.assertIn('ProLiant DL380 G7', str(e)) + @mock.patch.object(common, 'wait_for_ilo_after_reset') @mock.patch.object(ribcl.RIBCLOperations, '_request_ilo') - def test_reset_ilo(self, request_ilo_mock): + def test_reset_ilo(self, request_ilo_mock, status_mock): request_ilo_mock.return_value = constants.RESET_ILO_XML self.ilo.reset_ilo() self.assertTrue(request_ilo_mock.called) + status_mock.assert_called_once_with(self.ilo) @mock.patch.object(ribcl.RIBCLOperations, '_request_ilo') def test_reset_ilo_credential(self, request_ilo_mock):