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. # under the License.
"""IloClient module""" """IloClient module"""
import netaddr
from proliantutils import exception from proliantutils import exception
from proliantutils.ilo import ipmi from proliantutils.ilo import ipmi
from proliantutils.ilo import operations from proliantutils.ilo import operations
@ -125,9 +126,18 @@ class IloClient(operations.IloOperations):
def __init__(self, host, login, password, timeout=60, port=443, def __init__(self, host, login, password, timeout=60, port=443,
bios_password=None, cacert=None, snmp_credentials=None, bios_password=None, cacert=None, snmp_credentials=None,
use_redfish_only=False): 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, self.ribcl = ribcl.RIBCLOperations(host, login, password, timeout,
port, cacert=cacert) port, cacert=cacert)
self.info = {'address': host, 'username': login, 'password': password}
self.host = host self.host = host
self.use_redfish_only = use_redfish_only 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 # 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 # as we still want to try getting nic capacity through ipmitool
# irrespective of what firmware we are using. # 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: if nic_capacity:
capabilities.update({'nic_capacity': nic_capacity}) capabilities.update({'nic_capacity': nic_capacity})

View File

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

View File

@ -15,6 +15,7 @@
__author__ = 'HPE' __author__ = 'HPE'
import retrying import retrying
from six.moves.urllib.parse import urlparse
from sushy import connector from sushy import connector
from sushy import exceptions from sushy import exceptions
@ -44,4 +45,11 @@ class HPEConnector(connector.Connector):
:param headers: Optional dictionary of headers. :param headers: Optional dictionary of headers.
:returns: The response from the connector.Connector's _op method. :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') "1.2.3.4", "admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual( self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"}, {'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) self.assertEqual('product', c.model)
@mock.patch.object(ribcl, 'RIBCLOperations') @mock.patch.object(ribcl, 'RIBCLOperations')
@ -70,7 +120,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere') cacert='/somewhere')
self.assertEqual( self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"}, {'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info) c.ipmi_host_info)
self.assertEqual('ProLiant DL180 Gen10', c.model) self.assertEqual('ProLiant DL180 Gen10', c.model)
self.assertIsNotNone(c.redfish) self.assertIsNotNone(c.redfish)
self.assertTrue(c.is_ribcl_enabled) self.assertTrue(c.is_ribcl_enabled)
@ -97,7 +147,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere') cacert='/somewhere')
self.assertEqual( self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"}, {'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info) c.ipmi_host_info)
self.assertIsNotNone(c.model) self.assertIsNotNone(c.model)
self.assertIsNotNone(c.redfish) self.assertIsNotNone(c.redfish)
self.assertFalse(c.is_ribcl_enabled) self.assertFalse(c.is_ribcl_enabled)
@ -119,7 +169,7 @@ class IloClientInitTestCase(testtools.TestCase):
cacert='/somewhere') cacert='/somewhere')
self.assertEqual( self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"}, {'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info) c.ipmi_host_info)
self.assertIsNotNone(c.model) self.assertIsNotNone(c.model)
self.assertIsNotNone(c.redfish) self.assertIsNotNone(c.redfish)
self.assertIsNone(c.is_ribcl_enabled) self.assertIsNone(c.is_ribcl_enabled)
@ -152,7 +202,7 @@ class IloClientInitTestCase(testtools.TestCase):
"1.2.3.4", "admin", "Admin", 120, 4430, cacert='/somewhere') "1.2.3.4", "admin", "Admin", 120, 4430, cacert='/somewhere')
self.assertEqual( self.assertEqual(
{'address': "1.2.3.4", 'username': "admin", 'password': "Admin"}, {'address': "1.2.3.4", 'username': "admin", 'password': "Admin"},
c.info) c.ipmi_host_info)
self.assertEqual('product', c.model) self.assertEqual('product', c.model)
self.assertTrue(snmp_mock.called) self.assertTrue(snmp_mock.called)
@ -599,9 +649,9 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2', 'pci_gpu_devices': '2',
'nic_capacity': '10Gb'} 'nic_capacity': '10Gb'}
cap_mock.assert_called_once_with() 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(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(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations, @mock.patch.object(ribcl.RIBCLOperations,
@ -622,9 +672,9 @@ class IloClientTestCase(testtools.TestCase):
'server_model': 'Gen8', 'server_model': 'Gen8',
'pci_gpu_devices': '2'} 'pci_gpu_devices': '2'}
cap_mock.assert_called_once_with() 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(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(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations, @mock.patch.object(ribcl.RIBCLOperations,
@ -642,7 +692,7 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2'} 'pci_gpu_devices': '2'}
capabilities = self.client.get_server_capabilities() capabilities = self.client.get_server_capabilities()
self.assertEqual(expected_capabilities, 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(ipmi, 'get_nic_capacity')
@mock.patch.object(ribcl.RIBCLOperations, @mock.patch.object(ribcl.RIBCLOperations,
@ -660,7 +710,7 @@ class IloClientTestCase(testtools.TestCase):
'pci_gpu_devices': '2'} 'pci_gpu_devices': '2'}
capabilities = self.client.get_server_capabilities() capabilities = self.client.get_server_capabilities()
self.assertEqual(expected_capabilities, 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, @mock.patch.object(ris.RISOperations,
'get_ilo_firmware_version_as_major_minor') 'get_ilo_firmware_version_as_major_minor')
@ -679,7 +729,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'} 'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities() capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with() 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', expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x', 'rom_firmware_version': 'x',
'server_model': 'Gen9', 'server_model': 'Gen9',
@ -702,7 +752,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'} 'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities() capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with() 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', expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x', 'rom_firmware_version': 'x',
'server_model': 'Gen9', 'server_model': 'Gen9',
@ -744,7 +794,7 @@ class IloClientTestCase(testtools.TestCase):
'secure_boot': 'true'} 'secure_boot': 'true'}
capabilities = self.client.get_server_capabilities() capabilities = self.client.get_server_capabilities()
cap_mock.assert_called_once_with() 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', expected_capabilities = {'ilo_firmware_version': '2.10',
'rom_firmware_version': 'x', 'rom_firmware_version': 'x',
'server_model': 'Gen9', 'server_model': 'Gen9',
@ -1087,8 +1137,8 @@ class IloClientTestCase(testtools.TestCase):
snmp_mock.return_value = 250 snmp_mock.return_value = 250
self.client.get_essential_properties() self.client.get_essential_properties()
call_mock.assert_called_once_with('get_essential_properties') call_mock.assert_called_once_with('get_essential_properties')
snmp_mock.assert_called_once_with(self.client.info['address'], snmp_mock.assert_called_once_with(
snmp_credentials) self.client.ipmi_host_info['address'], snmp_credentials)
self.assertTrue(mac_mock.called) self.assertTrue(mac_mock.called)
@mock.patch.object(ris.RISOperations, 'get_active_macs') @mock.patch.object(ris.RISOperations, 'get_active_macs')
@ -1112,8 +1162,8 @@ class IloClientTestCase(testtools.TestCase):
snmp_mock.return_value = 0 snmp_mock.return_value = 0
self.client.get_essential_properties() self.client.get_essential_properties()
call_mock.assert_called_once_with('get_essential_properties') call_mock.assert_called_once_with('get_essential_properties')
snmp_mock.assert_called_once_with(self.client.info['address'], snmp_mock.assert_called_once_with(
snmp_credentials) self.client.ipmi_host_info['address'], snmp_credentials)
self.assertTrue(mac_mock.called) self.assertTrue(mac_mock.called)
@mock.patch.object(ris.RISOperations, 'get_active_macs') @mock.patch.object(ris.RISOperations, 'get_active_macs')

View File

@ -33,3 +33,8 @@ class IloOperationsTestCase(unittest.TestCase):
def test__no_host(self): def test__no_host(self):
self.assertEqual('[iLO <unknown>] foo', self.assertEqual('[iLO <unknown>] foo',
self.operations_object._('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) @mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_no_exception(self, conn_mock): def test__op_no_exception(self, conn_mock):
conn_mock.side_effect = ["Hello", exceptions.ConnectionError, response = mock.MagicMock()
"Hello", "World"] type(response).status_code = mock.PropertyMock(return_value=200)
conn_mock.side_effect = [response, exceptions.ConnectionError,
response, response]
hpe_conn = hpe_connector.HPEConnector( hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True) 'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'} headers = {'X-Fake': 'header'}
hpe_conn._op('GET', path='fake/path', data=None, headers=headers) hpe_conn._op('GET', path='fake/path', data=None, headers=headers)
conn_mock.assert_called_once_with(hpe_conn, 'GET', path='fake/path', 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) self.assertEqual(1, conn_mock.call_count)
@mock.patch.object(connector.Connector, '_op', autospec=True) @mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_with_exception(self, conn_mock): 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, conn_mock.side_effect = [exceptions.ConnectionError,
exceptions.ConnectionError, "Hello", "World"] exceptions.ConnectionError,
response, response]
hpe_conn = hpe_connector.HPEConnector( hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True) 'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'} headers = {'X-Fake': 'header'}
lval = hpe_conn._op('GET', path='fake/path', data=None, lval = hpe_conn._op('GET', path='fake/path', data=None,
headers=headers) headers=headers)
self.assertEqual(3, conn_mock.call_count) 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) @mock.patch.object(connector.Connector, '_op', autospec=True)
def test__op_all_exception(self, conn_mock): def test__op_all_exception(self, conn_mock):
@ -58,7 +62,6 @@ class HPEConnectorTestCase(testtools.TestCase):
exceptions.ConnectionError] * ( exceptions.ConnectionError] * (
hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS) + ( hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS) + (
["Hello", "World"]) ["Hello", "World"])
hpe_conn = hpe_connector.HPEConnector( hpe_conn = hpe_connector.HPEConnector(
'http://foo.bar:1234', verify=True) 'http://foo.bar:1234', verify=True)
headers = {'X-Fake': 'header'} headers = {'X-Fake': 'header'}
@ -67,3 +70,26 @@ class HPEConnectorTestCase(testtools.TestCase):
'GET', path='fake/path', data=None, headers=headers) 'GET', path='fake/path', data=None, headers=headers)
self.assertEqual(hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS, self.assertEqual(hpe_connector.HPEConnector.MAX_RETRY_ATTEMPTS,
conn_mock.call_count) 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)