Added support for IPv6 network

This commit added ilo client changes and redfish connector changes
to enable support for IPv6 network.

Change-Id: Ifefc1f0ee190d5420c0c63ae7674350facde8e27
This commit is contained in:
paresh-sao 2018-09-24 08:22:46 +00:00
parent 7286d7019c
commit 254318f608
6 changed files with 130 additions and 30 deletions

View File

@ -13,6 +13,7 @@
# under the License.
"""IloClient module"""
import netaddr
from proliantutils import exception
from proliantutils.ilo import ipmi
from proliantutils.ilo import operations
@ -125,9 +126,18 @@ class IloClient(operations.IloOperations):
def __init__(self, host, login, password, timeout=60, port=443,
bios_password=None, cacert=None, snmp_credentials=None,
use_redfish_only=False):
# IPv6 Check
# TODO(paresh) Need to test with Global IPv6 address
# IPMI supports IPv6 without square brackets
self.ipmi_host_info = {'address': host, 'username': login,
'password': password}
if netaddr.valid_ipv6(host.split('%')[0]):
host = '[' + host + ']'
self.ribcl = ribcl.RIBCLOperations(host, login, password, timeout,
port, cacert=cacert)
self.info = {'address': host, 'username': login, 'password': password}
self.host = host
self.use_redfish_only = use_redfish_only
@ -640,7 +650,8 @@ class IloClient(operations.IloOperations):
# NOTE(vmud213): Even if it is None, pass it on to get_nic_capacity
# as we still want to try getting nic capacity through ipmitool
# irrespective of what firmware we are using.
nic_capacity = ipmi.get_nic_capacity(self.info, major_minor)
nic_capacity = ipmi.get_nic_capacity(self.ipmi_host_info,
major_minor)
if nic_capacity:
capabilities.update({'nic_capacity': nic_capacity})

View File

@ -28,7 +28,7 @@ class IloOperations(object):
def _(self, msg):
"""Prepends host information if available to msg and returns it."""
try:
return "[iLO %s] %s" % (self.host, msg)
return "[iLO %s] %s" % (self.host.replace('%', '%%'), msg)
except AttributeError:
return "[iLO <unknown>] %s" % msg

View File

@ -15,6 +15,7 @@
__author__ = 'HPE'
import retrying
from six.moves.urllib.parse import urlparse
from sushy import connector
from sushy import exceptions
@ -44,4 +45,11 @@ class HPEConnector(connector.Connector):
:param headers: Optional dictionary of headers.
:returns: The response from the connector.Connector's _op method.
"""
return super(HPEConnector, self)._op(method, path, data, headers)
resp = super(HPEConnector, self)._op(method, path, data,
headers, allow_redirects=False)
# With IPv6, Gen10 server gives redirection response with new path with
# a prefix of '/' so this check is required
if resp.status_code == 308:
path = urlparse(resp.headers['Location']).path
resp = super(HPEConnector, self)._op(method, path, data, headers)
return resp

View File

@ -47,7 +47,57 @@ class IloClientInitTestCase(testtools.TestCase):
"1.2.3.4", "admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info)
c.ipmi_host_info)
self.assertEqual('product', c.model)
@mock.patch.object(ribcl, 'RIBCLOperations')
@mock.patch.object(ris, 'RISOperations')
def test_init_for_ipv6_link_address(self, ris_mock, ribcl_mock):
ribcl_obj_mock = mock.MagicMock()
ribcl_mock.return_value = ribcl_obj_mock
ribcl_obj_mock.get_product_name.return_value = 'product'
c = client.IloClient("FE80::9AF2:B3FF:FEEE:F884%eth0", "admin",
"Admin", timeout=120, port=4430,
bios_password='foo',
cacert='/somewhere')
ris_mock.assert_called_once_with(
"[FE80::9AF2:B3FF:FEEE:F884%eth0]",
"admin", "Admin", bios_password='foo',
cacert='/somewhere')
ribcl_mock.assert_called_once_with(
"[FE80::9AF2:B3FF:FEEE:F884%eth0]",
"admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual(
{'address': "FE80::9AF2:B3FF:FEEE:F884%eth0",
'username': "admin", 'password': "Admin"},
c.ipmi_host_info)
self.assertEqual('product', c.model)
@mock.patch.object(ribcl, 'RIBCLOperations')
@mock.patch.object(ris, 'RISOperations')
def test_init_for_ipv6_global_address(self, ris_mock, ribcl_mock):
ribcl_obj_mock = mock.MagicMock()
ribcl_mock.return_value = ribcl_obj_mock
ribcl_obj_mock.get_product_name.return_value = 'product'
c = client.IloClient("2001:0db8:85a3::8a2e:0370:7334", "admin",
"Admin", timeout=120, port=4430,
bios_password='foo',
cacert='/somewhere')
ris_mock.assert_called_once_with(
"[2001:0db8:85a3::8a2e:0370:7334]",
"admin", "Admin", bios_password='foo',
cacert='/somewhere')
ribcl_mock.assert_called_once_with(
"[2001:0db8:85a3::8a2e:0370:7334]",
"admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual(
{'address': "2001:0db8:85a3::8a2e:0370:7334",
'username': "admin", 'password': "Admin"},
c.ipmi_host_info)
self.assertEqual('product', c.model)
@mock.patch.object(ribcl, 'RIBCLOperations')
@ -70,7 +120,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere')
self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info)
c.ipmi_host_info)
self.assertEqual('ProLiant DL180 Gen10', c.model)
self.assertIsNotNone(c.redfish)
self.assertTrue(c.is_ribcl_enabled)
@ -97,7 +147,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere')
self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info)
c.ipmi_host_info)
self.assertIsNotNone(c.model)
self.assertIsNotNone(c.redfish)
self.assertFalse(c.is_ribcl_enabled)
@ -119,7 +169,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere')
self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info)
c.ipmi_host_info)
self.assertIsNotNone(c.model)
self.assertIsNotNone(c.redfish)
self.assertIsNone(c.is_ribcl_enabled)
@ -152,7 +202,7 @@ class IloClientInitTestCase(testtools.TestCase):
"1.2.3.4", "admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info)
c.ipmi_host_info)
self.assertEqual('product', c.model)
self.assertTrue(snmp_mock.called)
@ -599,9 +649,9 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2',
'nic_capacity': '10Gb'}
cap_mock.assert_called_once_with()
nic_mock.assert_called_once_with(self.client.info, str_val)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, str_val)
self.assertEqual(expected_capabilities, capabilities)
self.assertEqual(info, self.client.info)
self.assertEqual(info, self.client.ipmi_host_info)
@mock.patch.object(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations,
@ -622,9 +672,9 @@ class IloClientTestCase(testtools.TestCase):
'server_model': 'Gen8',
'pci_gpu_devices': '2'}
cap_mock.assert_called_once_with()
nic_mock.assert_called_once_with(self.client.info, str_val)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, str_val)
self.assertEqual(expected_capabilities, capabilities)
self.assertEqual(info, self.client.info)
self.assertEqual(info, self.client.ipmi_host_info)
@mock.patch.object(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations,
@ -642,7 +692,7 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2'}
capabilities = self.client.get_server_capabilities()
self.assertEqual(expected_capabilities, capabilities)
nic_mock.assert_called_once_with(self.client.info, None)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, None)
@mock.patch.object(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations,
@ -660,7 +710,7 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2'}
capabilities = self.client.get_server_capabilities()
self.assertEqual(expected_capabilities, capabilities)
nic_mock.assert_called_once_with(self.client.info, None)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, None)
@mock.patch.object(ris.RISOperations,
'get_ilo_firmware_version_as_major_minor')
@ -679,7 +729,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with()
nic_mock.assert_called_once_with(self.client.info, str_val)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, str_val)
expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x',
'server_model': 'Gen9',
@ -702,7 +752,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with()
nic_mock.assert_called_once_with(self.client.info, str_val)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, str_val)
expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x',
'server_model': 'Gen9',
@ -744,7 +794,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with()
nic_mock.assert_called_once_with(self.client.info, str_val)
nic_mock.assert_called_once_with(self.client.ipmi_host_info, str_val)
expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x',
'server_model': 'Gen9',
@ -1087,8 +1137,8 @@ class IloClientTestCase(testtools.TestCase):
snmp_mock.return_value = 250
self.client.get_essential_properties()
call_mock.assert_called_once_with('get_essential_properties')
snmp_mock.assert_called_once_with(self.client.info['address'],
snmp_credentials)
snmp_mock.assert_called_once_with(
self.client.ipmi_host_info['address'], snmp_credentials)
self.assertTrue(mac_mock.called)
@mock.patch.object(ris.RISOperations, 'get_active_macs')
@ -1112,8 +1162,8 @@ class IloClientTestCase(testtools.TestCase):
snmp_mock.return_value = 0
self.client.get_essential_properties()
call_mock.assert_called_once_with('get_essential_properties')
snmp_mock.assert_called_once_with(self.client.info['address'],
snmp_credentials)
snmp_mock.assert_called_once_with(
self.client.ipmi_host_info['address'], snmp_credentials)
self.assertTrue(mac_mock.called)
@mock.patch.object(ris.RISOperations, 'get_active_macs')

View File

@ -33,3 +33,8 @@ class IloOperationsTestCase(unittest.TestCase):
def test__no_host(self):
self.assertEqual('[iLO <unknown>] foo',
self.operations_object._('foo'))
def test__ipv6_host(self):
self.operations_object.host = 'FE80::9EB6:54FF:FEB1:ACEE%ens37'
self.assertEqual('[iLO FE80::9EB6:54FF:FEB1:ACEE%%ens37] foo',
self.operations_object._('foo'))

View File

@ -28,29 +28,33 @@ class HPEConnectorTestCase(testtools.TestCase):
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_no_exception(self, conn_mock):
conn_mock.side_effect = ["Hello", exceptions.ConnectionError,
"Hello", "World"]
response = mock.MagicMock()
type(response).status_code = mock.PropertyMock(return_value=200)
conn_mock.side_effect = [response, exceptions.ConnectionError,
response, response]
hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'}
hpe_conn._op('GET', path='fake/path', data=None, headers=headers)
conn_mock.assert_called_once_with(hpe_conn, 'GET', path='fake/path',
data=None, headers=headers)
data=None, headers=headers,
allow_redirects=False)
self.assertEqual(1, conn_mock.call_count)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_with_exception(self, conn_mock):
response = mock.MagicMock()
type(response).status_code = mock.PropertyMock(return_value=501)
conn_mock.side_effect = [exceptions.ConnectionError,
exceptions.ConnectionError, "Hello", "World"]
exceptions.ConnectionError,
response, response]
hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'}
lval = hpe_conn._op('GET', path='fake/path', data=None,
headers=headers)
self.assertEqual(3, conn_mock.call_count)
self.assertEqual(lval, "Hello")
self.assertEqual(lval.status_code, 501)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_all_exception(self, conn_mock):
@ -58,7 +62,6 @@ class HPEConnectorTestCase(testtools.TestCase):
exceptions.ConnectionError] * (
hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS) + (
["Hello", "World"])
hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'}
@ -67,3 +70,26 @@ class HPEConnectorTestCase(testtools.TestCase):
'GET', path='fake/path', data=None, headers=headers)
self.assertEqual(hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS,
conn_mock.call_count)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_with_redirection_false_status_308(self, conn_mock):
response = mock.MagicMock()
type(response).status_code = mock.PropertyMock(return_value=308)
headers = {'X-Fake': 'header',
'Location': 'http://foo.bar:1234/new/path'}
type(response).headers = headers
response_redirect = mock.MagicMock()
type(response_redirect).status_code = (
mock.PropertyMock(return_value=200))
conn_mock.side_effect = [response, response_redirect]
hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'}
res = hpe_conn._op('GET', path='fake/path',
data=None, headers=headers)
calls = [mock.call(hpe_conn, 'GET', path='fake/path', data=None,
headers=headers, allow_redirects=False),
mock.call(hpe_conn, 'GET', path='/new/path', data=None,
headers=headers)]
conn_mock.assert_has_calls(calls)
self.assertEqual(res.status_code, 200)