From 3a5288a9adcb184ac4cc371816dc2ffa18695bbb Mon Sep 17 00:00:00 2001 From: Hugo Nicodemos Date: Thu, 29 Jun 2017 16:53:47 -0300 Subject: [PATCH] Introduce hpOneView and ilorest to OneView It introduces the hpOneView and ilorest library to the OneView Driver. This patch will be used as the standard patch to other patches related to the removal of the python-oneviewclient library dependency. Partial-Bug: #1693788 Change-Id: I77a220e8dc7099850aa5edb7a51e19ae486675a9 --- driver-requirements.txt | 2 + ironic/drivers/modules/oneview/common.py | 79 ++++++++++++++++++- .../drivers/modules/oneview/test_common.py | 29 +++++++ .../drivers/modules/oneview/test_inspect.py | 2 + .../drivers/third_party_driver_mock_specs.py | 18 +++++ .../unit/drivers/third_party_driver_mocks.py | 17 ++++ ...uce-hponeview-client-6652d468034963b2.yaml | 6 ++ 7 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/introduce-hponeview-client-6652d468034963b2.yaml diff --git a/driver-requirements.txt b/driver-requirements.txt index 356deba03e..b707d9c0c3 100644 --- a/driver-requirements.txt +++ b/driver-requirements.txt @@ -9,6 +9,8 @@ pysnmp python-ironic-inspector-client>=1.5.0 python-oneviewclient<3.0.0,>=2.5.2 python-scciclient>=0.5.0 +python-ilorest-library>=1.9.2 +hpOneView<4.0.0,>=3.2.1 UcsSdk==0.8.2.2 python-dracclient>=1.3.0 diff --git a/ironic/drivers/modules/oneview/common.py b/ironic/drivers/modules/oneview/common.py index dfc62b9bc8..8c16895b09 100644 --- a/ironic/drivers/modules/oneview/common.py +++ b/ironic/drivers/modules/oneview/common.py @@ -13,9 +13,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - from oslo_log import log as logging from oslo_utils import importutils +from six.moves.urllib import parse from ironic.common import exception from ironic.common.i18n import _ @@ -25,11 +25,18 @@ from ironic.drivers import utils LOG = logging.getLogger(__name__) +# NOTE(mrtenio): hpOneView will be the default library for OneView. It +# is being introduced together with the python-oneviewclient to be used +# generally by other patches. python-oneviewclient will be removed +# subsequently. client = importutils.try_import('oneview_client.client') oneview_utils = importutils.try_import('oneview_client.utils') oneview_states = importutils.try_import('oneview_client.states') oneview_exceptions = importutils.try_import('oneview_client.exceptions') +hponeview_client = importutils.try_import('hpOneView.oneview_client') +redfish = importutils.try_import('redfish') + REQUIRED_ON_DRIVER_INFO = { 'server_hardware_uri': _("Server Hardware URI. Required in driver_info."), } @@ -49,6 +56,8 @@ OPTIONAL_ON_PROPERTIES = { "Enclosure Group URI. Optional in properties/capabilities."), } +ILOREST_BASE_PORT = "443" + COMMON_PROPERTIES = {} COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO) COMMON_PROPERTIES.update(REQUIRED_ON_PROPERTIES) @@ -83,6 +92,74 @@ def get_oneview_client(): return oneview_client +def prepare_manager_url(manager_url): + # NOTE(mrtenio) python-oneviewclient uses https or http in the manager_url + # while python-hpOneView does not. This will not be necessary when + # python-hpOneView client is the only OneView library. + if manager_url: + url_parse = parse.urlparse(manager_url) + manager_url = url_parse.netloc + return manager_url + + +def get_hponeview_client(): + """Generate an instance of the hpOneView client. + + Generates an instance of the hpOneView client using the hpOneView library. + + :returns: an instance of the OneViewClient + :raises: InvalidParameterValue if mandatory information is missing on the + node or on invalid input. + """ + manager_url = prepare_manager_url(CONF.oneview.manager_url) + config = { + "ip": manager_url, + "credentials": { + "userName": CONF.oneview.username, + "password": CONF.oneview.password + } + } + return hponeview_client.OneViewClient(config) + + +def get_ilorest_client(oneview_client, server_hardware): + """Generate an instance of the iLORest library client. + + :param oneview_client: an instance of a python-hpOneView + :param: server_hardware: a server hardware uuid or uri + :returns: an instance of the iLORest client + :raises: InvalidParameterValue if mandatory information is missing on the + node or on invalid input. + """ + remote_console = oneview_client.server_hardware.get_remote_console_url( + server_hardware + ) + host_ip, ilo_token = _get_ilo_access(remote_console) + base_url = "https://%s:%s" % (host_ip, ILOREST_BASE_PORT) + return redfish.rest_client(base_url=base_url, sessionkey=ilo_token) + + +def _get_ilo_access(remote_console): + """Get the needed information to access ilo. + + Get the host_ip and a token of an iLO remote console instance which can be + used to perform operations on that controller. + + The Remote Console url has the following format: + hplocons://addr=1.2.3.4&sessionkey=a79659e3b3b7c8209c901ac3509a6719 + + :param: remote_console: OneView Remote Console object with a + remoteConsoleUrl + :returns: A tuple with the Host IP and Token to access ilo, for + example: ('1.2.3.4', 'a79659e3b3b7c8209c901ac3509a6719') + """ + url = remote_console.get('remoteConsoleUrl') + url_parse = parse.urlparse(url) + host_ip = parse.parse_qs(url_parse.netloc).get('addr')[0] + token = parse.parse_qs(url_parse.netloc).get('sessionkey')[0] + return host_ip, token + + def verify_node_info(node): """Verifies if fields and namespaces of a node are valid. diff --git a/ironic/tests/unit/drivers/modules/oneview/test_common.py b/ironic/tests/unit/drivers/modules/oneview/test_common.py index d42d819fa6..5fa2a8553a 100644 --- a/ironic/tests/unit/drivers/modules/oneview/test_common.py +++ b/ironic/tests/unit/drivers/modules/oneview/test_common.py @@ -25,6 +25,7 @@ from ironic.tests.unit.db import base as db_base from ironic.tests.unit.db import utils as db_utils from ironic.tests.unit.objects import utils as obj_utils +hponeview_client = importutils.try_import('oneview_client.client') oneview_states = importutils.try_import('oneview_client.states') @@ -58,6 +59,34 @@ class OneViewCommonTestCase(db_base.DbTestCase): "server_hardware_type_uri"): common.verify_node_info(self.node) + def test_prepare_manager_url(self): + self.assertEqual( + common.prepare_manager_url("https://1.2.3.4/"), "1.2.3.4") + self.assertEqual( + common.prepare_manager_url("http://oneview"), "oneview") + self.assertEqual( + common.prepare_manager_url("http://oneview:8080"), "oneview:8080") + self.assertEqual( + common.prepare_manager_url("http://oneview/something"), "oneview") + self.assertEqual( + common.prepare_manager_url("oneview/something"), "") + self.assertEqual( + common.prepare_manager_url("oneview"), "") + + @mock.patch.object(hponeview_client, 'OneViewClient', spec_set=True, + autospec=True) + def test_get_hponeview_client(self, mock_hponeview_client): + common.get_hponeview_client() + mock_hponeview_client.assert_called_once_with(self.config) + + def test_get_ilo_access(self): + url = ("hplocons://addr=1.2.3.4&sessionkey" + + "=a79659e3b3b7c8209c901ac3509a6719") + remote_console = {'remoteConsoleUrl': url} + host_ip, token = common._get_ilo_access(remote_console) + self.assertEqual(host_ip, "1.2.3.4") + self.assertEqual(token, "a79659e3b3b7c8209c901ac3509a6719") + def test_verify_node_info_missing_node_driver_info(self): self.node.driver_info = {} diff --git a/ironic/tests/unit/drivers/modules/oneview/test_inspect.py b/ironic/tests/unit/drivers/modules/oneview/test_inspect.py index b574550d04..d676b5e74e 100644 --- a/ironic/tests/unit/drivers/modules/oneview/test_inspect.py +++ b/ironic/tests/unit/drivers/modules/oneview/test_inspect.py @@ -31,6 +31,7 @@ class AgentPXEOneViewInspectTestCase(db_base.DbTestCase): def setUp(self): super(AgentPXEOneViewInspectTestCase, self).setUp() + self.config(manager_url='https://1.2.3.4', group='oneview') self.config(enabled=True, group='inspector') mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview") self.node = obj_utils.create_test_node( @@ -69,6 +70,7 @@ class ISCSIPXEOneViewInspectTestCase(db_base.DbTestCase): def setUp(self): super(ISCSIPXEOneViewInspectTestCase, self).setUp() + self.config(manager_url='https://1.2.3.4', group='oneview') self.config(enabled=True, group='inspector') mgr_utils.mock_the_extension_manager(driver="iscsi_pxe_oneview") self.node = obj_utils.create_test_node( diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index 8f59f10720..4e7e8b7c05 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -143,6 +143,24 @@ ONEVIEWCLIENT_STATES_SPEC = ( 'ONEVIEW_ERROR', ) +HPONEVIEW_SPEC = ( + 'oneview_client', + 'resources', + 'exceptions', +) + +HPONEVIEW_CLS_SPEC = ( +) + +HPONEVIEW_STATES_SPEC = ( + 'ONEVIEW_POWER_OFF', + 'ONEVIEW_POWERING_OFF', + 'ONEVIEW_POWER_ON', + 'ONEVIEW_POWERING_ON', + 'ONEVIEW_RESETTING', + 'ONEVIEW_ERROR', +) + SUSHY_CONSTANTS_SPEC = ( 'BOOT_SOURCE_TARGET_PXE', 'BOOT_SOURCE_TARGET_HDD', diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index 31fec78b75..ed5992f504 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -26,6 +26,7 @@ Current list of mocked libraries: - pysnmp - scciclient - oneview_client +- hpOneView - pywsman - python-dracclient """ @@ -99,6 +100,22 @@ if 'ironic.drivers.oneview' in sys.modules: six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview']) +hpOneView = importutils.try_import('hpOneView') +if not hpOneView: + hpOneView = mock.MagicMock(spec_set=mock_specs.HPONEVIEW_SPEC) + sys.modules['hpOneView'] = hpOneView + sys.modules['hpOneView.oneview_client'] = hpOneView.oneview_client + sys.modules['hpOneView.resources'] = hpOneView.resources + sys.modules['hpOneView.exceptions'] = hpOneView.exceptions + hpOneView.exceptions.HPOneViewException = type('HPOneViewException', + (Exception,), {}) +sys.modules['hpOneView.oneview_client'].OneViewClient = mock.MagicMock( + spec_set=mock_specs.HPONEVIEW_CLS_SPEC +) +if 'ironic.drivers.oneview' in sys.modules: + six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview']) + + # attempt to load the external 'python-dracclient' library, which is required # by the optional drivers.modules.drac module dracclient = importutils.try_import('dracclient') diff --git a/releasenotes/notes/introduce-hponeview-client-6652d468034963b2.yaml b/releasenotes/notes/introduce-hponeview-client-6652d468034963b2.yaml new file mode 100644 index 0000000000..12ba47f333 --- /dev/null +++ b/releasenotes/notes/introduce-hponeview-client-6652d468034963b2.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Adds the ``hpOneView`` and ``ilorest`` library requirement + to the OneView Driver, in order to prepare for the eventual + removal of dependency on ``python-oneviewclient`` library.