Start passing portgroup information to Neutron
With this patch ironic starts passing portgroup information to Neutron via 'binding:profile'. This will allow to configure portgroup on the switch by appropriate ML2 driver during deployment. The example of binding:profile dictionary is: { 'local_link_information':[ { 'switch_id': 'aa:bb:cc:dd:ee:ff', 'port_id': 'Gig0/1' }, { 'switch_id': 'aa:bb:cc:dd:ee:ff', 'port_id: 'Gig0/2' } ], 'local_group_information': { 'id': portgroup.uuid, 'name': portgroup.name, 'bond_mode': portgroup.mode, 'bond_properties': { 'bond_propertyA': 'valueA', 'bond_propertyB': 'valueB', } } } Partial-Bug: #1652630 Co-Authored-By: John L. Villalovos <john.l.villalovos@intel.com> Change-Id: Iacda8180f644cc1a0986e8b1fc34c65263aabd59
This commit is contained in:
parent
5ea8d9f354
commit
ade71e4f16
@ -328,7 +328,14 @@ def get_node_portmap(task):
|
||||
"""Extract the switch port information for the node.
|
||||
|
||||
:param task: a task containing the Node object.
|
||||
:returns: a dictionary in the form {port.uuid: port.local_link_connection}
|
||||
:returns: a dictionary in the form
|
||||
{
|
||||
port.uuid: {
|
||||
'switch_id': 'abc',
|
||||
'port_id': 'Po0/1',
|
||||
'other_llc_key': 'val'
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
portmap = {}
|
||||
@ -339,6 +346,41 @@ def get_node_portmap(task):
|
||||
# necessary info? (probably)
|
||||
|
||||
|
||||
def get_local_group_information(task, portgroup):
|
||||
"""Extract the portgroup information.
|
||||
|
||||
:param task: a task containing the Node object.
|
||||
:param portgroup: Ironic portgroup object to extract data for.
|
||||
:returns: a dictionary in the form:
|
||||
{
|
||||
'id': portgroup.uuid,
|
||||
'name': portgroup.name,
|
||||
'bond_mode': portgroup.mode,
|
||||
'bond_properties': {
|
||||
'bond_propertyA': 'valueA',
|
||||
'bond_propertyB': 'valueB',
|
||||
}
|
||||
{
|
||||
"""
|
||||
|
||||
portgroup_properties = {}
|
||||
for prop, value in portgroup.properties.items():
|
||||
# These properties are the bonding driver options described
|
||||
# at https://www.kernel.org/doc/Documentation/networking/bonding.txt .
|
||||
# cloud-init checks the same way, parameter name has to start with
|
||||
# 'bond'. Keep this structure when passing properties to neutron ML2
|
||||
# drivers.
|
||||
key = prop if prop.startswith('bond') else 'bond_%s' % prop
|
||||
portgroup_properties[key] = value
|
||||
|
||||
return {
|
||||
'id': portgroup.uuid,
|
||||
'name': portgroup.name,
|
||||
'bond_mode': portgroup.mode,
|
||||
'bond_properties': portgroup_properties
|
||||
}
|
||||
|
||||
|
||||
def rollback_ports(task, network_uuid):
|
||||
"""Attempts to delete any ports created by cleaning/provisioning
|
||||
|
||||
|
@ -231,6 +231,7 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
||||
|
||||
node = task.node
|
||||
local_link_info = []
|
||||
local_group_info = {}
|
||||
client_id_opt = None
|
||||
|
||||
vif_id = (
|
||||
@ -253,6 +254,8 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
||||
if p.portgroup_id == port_like_obj.id]
|
||||
for port in pg_ports:
|
||||
local_link_info.append(port.local_link_connection)
|
||||
local_group_info = neutron.get_local_group_information(
|
||||
task, port_like_obj)
|
||||
else:
|
||||
# We iterate only on ports or portgroups, no need to check
|
||||
# that it is a port
|
||||
@ -268,11 +271,13 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
||||
'port': {
|
||||
'binding:vnic_type': 'baremetal',
|
||||
'binding:host_id': node.uuid,
|
||||
'binding:profile': {
|
||||
'local_link_information': local_link_info,
|
||||
},
|
||||
}
|
||||
}
|
||||
binding_profile = {'local_link_information': local_link_info}
|
||||
if local_group_info:
|
||||
binding_profile['local_group_information'] = local_group_info
|
||||
body['port']['binding:profile'] = binding_profile
|
||||
|
||||
if client_id_opt:
|
||||
body['port']['extra_dhcp_opts'] = [client_id_opt]
|
||||
|
||||
|
@ -398,6 +398,25 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
portmap
|
||||
)
|
||||
|
||||
def test_get_local_group_information(self):
|
||||
pg = object_utils.create_test_portgroup(
|
||||
self.context, node_id=self.node.id,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
address='52:54:55:cf:2d:32',
|
||||
mode='802.3ad', properties={'bond_opt1': 'foo',
|
||||
'opt2': 'bar'},
|
||||
name='test-pg'
|
||||
)
|
||||
expected = {
|
||||
'id': pg.uuid,
|
||||
'name': pg.name,
|
||||
'bond_mode': pg.mode,
|
||||
'bond_properties': {'bond_opt1': 'foo', 'bond_opt2': 'bar'},
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
res = neutron.get_local_group_information(task, pg)
|
||||
self.assertEqual(expected, res)
|
||||
|
||||
@mock.patch.object(neutron, 'remove_ports_from_network')
|
||||
def test_rollback_ports(self, remove_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
|
@ -364,8 +364,11 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
||||
self.node.save()
|
||||
self._test_configure_tenant_networks(is_client_id=True)
|
||||
|
||||
@mock.patch.object(neutron_common, 'get_client')
|
||||
def test_configure_tenant_networks_with_portgroups(self, client_mock):
|
||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_local_group_information',
|
||||
autospec=True)
|
||||
def test_configure_tenant_networks_with_portgroups(
|
||||
self, glgi_mock, client_mock):
|
||||
pg = utils.create_test_portgroup(
|
||||
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
|
||||
extra={'vif_port_id': uuidutils.generate_uuid()})
|
||||
@ -387,6 +390,8 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
||||
)
|
||||
upd_mock = mock.Mock()
|
||||
client_mock.return_value.update_port = upd_mock
|
||||
local_group_info = {'a': 'b'}
|
||||
glgi_mock.return_value = local_group_info
|
||||
expected_body = {
|
||||
'port': {
|
||||
'binding:vnic_type': 'baremetal',
|
||||
@ -395,16 +400,22 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
||||
}
|
||||
call1_body = copy.deepcopy(expected_body)
|
||||
call1_body['port']['binding:profile'] = {
|
||||
'local_link_information': [self.port.local_link_connection]
|
||||
'local_link_information': [self.port.local_link_connection],
|
||||
}
|
||||
call2_body = copy.deepcopy(expected_body)
|
||||
call2_body['port']['binding:profile'] = {
|
||||
'local_link_information': [port1.local_link_connection,
|
||||
port2.local_link_connection]
|
||||
port2.local_link_connection],
|
||||
'local_group_information': local_group_info
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
# Override task.portgroups here, to have ability to check
|
||||
# that mocked get_local_group_information was called with
|
||||
# this portgroup object.
|
||||
task.portgroups = [pg]
|
||||
self.interface.configure_tenant_networks(task)
|
||||
client_mock.assert_called_once_with()
|
||||
glgi_mock.assert_called_once_with(task, pg)
|
||||
upd_mock.assert_has_calls(
|
||||
[mock.call(self.port.extra['vif_port_id'], call1_body),
|
||||
mock.call(pg.extra['vif_port_id'], call2_body)]
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Passes port group information (``portgroup.mode`` and
|
||||
``portgroup.properties``) to Neutron via Neutron ``port.binding:profile``
|
||||
field.
|
Loading…
Reference in New Issue
Block a user