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. Change-Id: Ib9d72ff5713d58631bcdccc817707b5d512e156e Partial-Bug: #1693788 Co-Authored-By: Fellype Cavalcante <fellypefca@lsd.ufcg.edu.br> Co-Authored-By: Ricardo Araujo <ricardo@lsd.ufcg.edu.br>
This commit is contained in:
parent
638943dbf8
commit
97a8ae1c77
@ -9,6 +9,8 @@ pysnmp
|
|||||||
python-ironic-inspector-client>=1.5.0
|
python-ironic-inspector-client>=1.5.0
|
||||||
python-oneviewclient<3.0.0,>=2.5.2
|
python-oneviewclient<3.0.0,>=2.5.2
|
||||||
python-scciclient>=0.6.0
|
python-scciclient>=0.6.0
|
||||||
|
python-ilorest-library>=2.1.0
|
||||||
|
hpOneView>=4.4.0
|
||||||
UcsSdk==0.8.2.2
|
UcsSdk==0.8.2.2
|
||||||
python-dracclient>=1.3.0
|
python-dracclient>=1.3.0
|
||||||
|
|
||||||
|
@ -13,8 +13,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common.i18n import _
|
from ironic.common.i18n import _
|
||||||
@ -29,6 +32,9 @@ oneview_utils = importutils.try_import('oneview_client.utils')
|
|||||||
oneview_states = importutils.try_import('oneview_client.states')
|
oneview_states = importutils.try_import('oneview_client.states')
|
||||||
oneview_exceptions = importutils.try_import('oneview_client.exceptions')
|
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 = {
|
REQUIRED_ON_DRIVER_INFO = {
|
||||||
'server_hardware_uri': _("Server Hardware URI. Required in driver_info."),
|
'server_hardware_uri': _("Server Hardware URI. Required in driver_info."),
|
||||||
}
|
}
|
||||||
@ -48,6 +54,8 @@ OPTIONAL_ON_PROPERTIES = {
|
|||||||
"Enclosure Group URI. Optional in properties/capabilities."),
|
"Enclosure Group URI. Optional in properties/capabilities."),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ILOREST_BASE_PORT = "443"
|
||||||
|
|
||||||
COMMON_PROPERTIES = {}
|
COMMON_PROPERTIES = {}
|
||||||
COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
COMMON_PROPERTIES.update(REQUIRED_ON_DRIVER_INFO)
|
||||||
COMMON_PROPERTIES.update(REQUIRED_ON_PROPERTIES)
|
COMMON_PROPERTIES.update(REQUIRED_ON_PROPERTIES)
|
||||||
@ -82,6 +90,90 @@ def get_oneview_client():
|
|||||||
return 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_match = "^(http[s]?://)?([^/]+)(/.*)?$"
|
||||||
|
manager_url = re.search(url_match, manager_url).group(2)
|
||||||
|
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.
|
||||||
|
:raises: OneViewError if try a secure connection without CA certificate.
|
||||||
|
"""
|
||||||
|
manager_url = prepare_manager_url(CONF.oneview.manager_url)
|
||||||
|
|
||||||
|
insecure = CONF.oneview.allow_insecure_connections
|
||||||
|
ssl_certificate = CONF.oneview.tls_cacert_file
|
||||||
|
|
||||||
|
if not (insecure or ssl_certificate):
|
||||||
|
msg = _("TLS CA certificate to connect with OneView is missing.")
|
||||||
|
raise exception.OneViewError(error=msg)
|
||||||
|
|
||||||
|
# NOTE(nicodemos) Ignore the CA certificate if it's an insecure connection
|
||||||
|
if insecure and ssl_certificate:
|
||||||
|
LOG.debug("Doing an insecure connection with OneView, the CA "
|
||||||
|
"certificate file: %s will be ignored.", ssl_certificate)
|
||||||
|
ssl_certificate = None
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"ip": manager_url,
|
||||||
|
"credentials": {
|
||||||
|
"userName": CONF.oneview.username,
|
||||||
|
"password": CONF.oneview.password
|
||||||
|
},
|
||||||
|
"ssl_certificate": ssl_certificate
|
||||||
|
}
|
||||||
|
return hponeview_client.OneViewClient(config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ilorest_client(server_hardware):
|
||||||
|
"""Generate an instance of the iLORest library client.
|
||||||
|
|
||||||
|
: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.
|
||||||
|
"""
|
||||||
|
oneview_client = get_hponeview_client()
|
||||||
|
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):
|
def verify_node_info(node):
|
||||||
"""Verifies if fields and namespaces of a node are valid.
|
"""Verifies if fields and namespaces of a node are valid.
|
||||||
|
|
||||||
|
@ -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.db import utils as db_utils
|
||||||
from ironic.tests.unit.objects import utils as obj_utils
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
hponeview_client = importutils.try_import('hpOneView.oneview_client')
|
||||||
oneview_states = importutils.try_import('oneview_client.states')
|
oneview_states = importutils.try_import('oneview_client.states')
|
||||||
|
|
||||||
|
|
||||||
@ -40,8 +41,54 @@ class OneViewCommonTestCase(db_base.DbTestCase):
|
|||||||
self.config(manager_url='https://1.2.3.4', group='oneview')
|
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||||
self.config(username='user', group='oneview')
|
self.config(username='user', group='oneview')
|
||||||
self.config(password='password', group='oneview')
|
self.config(password='password', group='oneview')
|
||||||
|
self.config(tls_cacert_file='ca_file', group='oneview')
|
||||||
|
self.config(allow_insecure_connections=False, group='oneview')
|
||||||
mgr_utils.mock_the_extension_manager(driver="fake_oneview")
|
mgr_utils.mock_the_extension_manager(driver="fake_oneview")
|
||||||
|
|
||||||
|
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"), "oneview")
|
||||||
|
self.assertEqual(
|
||||||
|
common.prepare_manager_url("oneview"), "oneview")
|
||||||
|
|
||||||
|
@mock.patch.object(hponeview_client, 'OneViewClient', 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_hponeview_client_insecure_false(self):
|
||||||
|
self.config(tls_cacert_file=None, group='oneview')
|
||||||
|
self.assertRaises(exception.OneViewError, common.get_hponeview_client)
|
||||||
|
|
||||||
|
@mock.patch.object(hponeview_client, 'OneViewClient', autospec=True)
|
||||||
|
def test_get_hponeview_client_insecure_cafile(self, mock_oneview):
|
||||||
|
self.config(allow_insecure_connections=True, group='oneview')
|
||||||
|
credentials = {
|
||||||
|
"ip": 'https://1.2.3.4',
|
||||||
|
"credentials": {
|
||||||
|
"userName": 'user',
|
||||||
|
"password": 'password'
|
||||||
|
},
|
||||||
|
"ssl_certificate": None
|
||||||
|
}
|
||||||
|
mock_oneview.assert_called_once_with(credentials)
|
||||||
|
|
||||||
|
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(self):
|
def test_verify_node_info(self):
|
||||||
common.verify_node_info(self.node)
|
common.verify_node_info(self.node)
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ class AgentPXEOneViewInspectTestCase(db_base.DbTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AgentPXEOneViewInspectTestCase, self).setUp()
|
super(AgentPXEOneViewInspectTestCase, self).setUp()
|
||||||
self.config(enabled=True, group='inspector')
|
self.config(enabled=True, group='inspector')
|
||||||
|
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||||
mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview")
|
mgr_utils.mock_the_extension_manager(driver="agent_pxe_oneview")
|
||||||
self.node = obj_utils.create_test_node(
|
self.node = obj_utils.create_test_node(
|
||||||
self.context, driver='agent_pxe_oneview',
|
self.context, driver='agent_pxe_oneview',
|
||||||
@ -69,6 +70,7 @@ class ISCSIPXEOneViewInspectTestCase(db_base.DbTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ISCSIPXEOneViewInspectTestCase, self).setUp()
|
super(ISCSIPXEOneViewInspectTestCase, self).setUp()
|
||||||
self.config(enabled=True, group='inspector')
|
self.config(enabled=True, group='inspector')
|
||||||
|
self.config(manager_url='https://1.2.3.4', group='oneview')
|
||||||
mgr_utils.mock_the_extension_manager(driver="iscsi_pxe_oneview")
|
mgr_utils.mock_the_extension_manager(driver="iscsi_pxe_oneview")
|
||||||
self.node = obj_utils.create_test_node(
|
self.node = obj_utils.create_test_node(
|
||||||
self.context, driver='iscsi_pxe_oneview',
|
self.context, driver='iscsi_pxe_oneview',
|
||||||
|
@ -144,6 +144,24 @@ ONEVIEWCLIENT_STATES_SPEC = (
|
|||||||
'ONEVIEW_ERROR',
|
'ONEVIEW_ERROR',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HPE_ONEVIEW_SPEC = (
|
||||||
|
'oneview_client',
|
||||||
|
'resources',
|
||||||
|
'exceptions',
|
||||||
|
)
|
||||||
|
|
||||||
|
HPE_ONEVIEW_CLS_SPEC = (
|
||||||
|
)
|
||||||
|
|
||||||
|
HPE_ONEVIEW_STATES_SPEC = (
|
||||||
|
'ONEVIEW_POWER_OFF',
|
||||||
|
'ONEVIEW_POWERING_OFF',
|
||||||
|
'ONEVIEW_POWER_ON',
|
||||||
|
'ONEVIEW_POWERING_ON',
|
||||||
|
'ONEVIEW_RESETTING',
|
||||||
|
'ONEVIEW_ERROR',
|
||||||
|
)
|
||||||
|
|
||||||
SUSHY_CONSTANTS_SPEC = (
|
SUSHY_CONSTANTS_SPEC = (
|
||||||
'BOOT_SOURCE_TARGET_PXE',
|
'BOOT_SOURCE_TARGET_PXE',
|
||||||
'BOOT_SOURCE_TARGET_HDD',
|
'BOOT_SOURCE_TARGET_HDD',
|
||||||
|
@ -26,6 +26,7 @@ Current list of mocked libraries:
|
|||||||
- pysnmp
|
- pysnmp
|
||||||
- scciclient
|
- scciclient
|
||||||
- oneview_client
|
- oneview_client
|
||||||
|
- hpOneView
|
||||||
- pywsman
|
- pywsman
|
||||||
- python-dracclient
|
- python-dracclient
|
||||||
"""
|
"""
|
||||||
@ -99,6 +100,22 @@ if 'ironic.drivers.oneview' in sys.modules:
|
|||||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.oneview'])
|
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.HPE_ONEVIEW_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.HPE_ONEVIEW_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
|
# attempt to load the external 'python-dracclient' library, which is required
|
||||||
# by the optional drivers.modules.drac module
|
# by the optional drivers.modules.drac module
|
||||||
dracclient = importutils.try_import('dracclient')
|
dracclient = importutils.try_import('dracclient')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user