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.
|
"""Extract the switch port information for the node.
|
||||||
|
|
||||||
:param task: a task containing the Node object.
|
: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 = {}
|
portmap = {}
|
||||||
@ -339,6 +346,41 @@ def get_node_portmap(task):
|
|||||||
# necessary info? (probably)
|
# 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):
|
def rollback_ports(task, network_uuid):
|
||||||
"""Attempts to delete any ports created by cleaning/provisioning
|
"""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
|
node = task.node
|
||||||
local_link_info = []
|
local_link_info = []
|
||||||
|
local_group_info = {}
|
||||||
client_id_opt = None
|
client_id_opt = None
|
||||||
|
|
||||||
vif_id = (
|
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]
|
if p.portgroup_id == port_like_obj.id]
|
||||||
for port in pg_ports:
|
for port in pg_ports:
|
||||||
local_link_info.append(port.local_link_connection)
|
local_link_info.append(port.local_link_connection)
|
||||||
|
local_group_info = neutron.get_local_group_information(
|
||||||
|
task, port_like_obj)
|
||||||
else:
|
else:
|
||||||
# We iterate only on ports or portgroups, no need to check
|
# We iterate only on ports or portgroups, no need to check
|
||||||
# that it is a port
|
# that it is a port
|
||||||
@ -268,11 +271,13 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
|||||||
'port': {
|
'port': {
|
||||||
'binding:vnic_type': 'baremetal',
|
'binding:vnic_type': 'baremetal',
|
||||||
'binding:host_id': node.uuid,
|
'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:
|
if client_id_opt:
|
||||||
body['port']['extra_dhcp_opts'] = [client_id_opt]
|
body['port']['extra_dhcp_opts'] = [client_id_opt]
|
||||||
|
|
||||||
|
@ -398,6 +398,25 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
|||||||
portmap
|
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')
|
@mock.patch.object(neutron, 'remove_ports_from_network')
|
||||||
def test_rollback_ports(self, remove_mock):
|
def test_rollback_ports(self, remove_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
@ -364,8 +364,11 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||||||
self.node.save()
|
self.node.save()
|
||||||
self._test_configure_tenant_networks(is_client_id=True)
|
self._test_configure_tenant_networks(is_client_id=True)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'get_client')
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
def test_configure_tenant_networks_with_portgroups(self, client_mock):
|
@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(
|
pg = utils.create_test_portgroup(
|
||||||
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
|
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
|
||||||
extra={'vif_port_id': uuidutils.generate_uuid()})
|
extra={'vif_port_id': uuidutils.generate_uuid()})
|
||||||
@ -387,6 +390,8 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||||||
)
|
)
|
||||||
upd_mock = mock.Mock()
|
upd_mock = mock.Mock()
|
||||||
client_mock.return_value.update_port = upd_mock
|
client_mock.return_value.update_port = upd_mock
|
||||||
|
local_group_info = {'a': 'b'}
|
||||||
|
glgi_mock.return_value = local_group_info
|
||||||
expected_body = {
|
expected_body = {
|
||||||
'port': {
|
'port': {
|
||||||
'binding:vnic_type': 'baremetal',
|
'binding:vnic_type': 'baremetal',
|
||||||
@ -395,16 +400,22 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
|||||||
}
|
}
|
||||||
call1_body = copy.deepcopy(expected_body)
|
call1_body = copy.deepcopy(expected_body)
|
||||||
call1_body['port']['binding:profile'] = {
|
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 = copy.deepcopy(expected_body)
|
||||||
call2_body['port']['binding:profile'] = {
|
call2_body['port']['binding:profile'] = {
|
||||||
'local_link_information': [port1.local_link_connection,
|
'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:
|
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)
|
self.interface.configure_tenant_networks(task)
|
||||||
client_mock.assert_called_once_with()
|
client_mock.assert_called_once_with()
|
||||||
|
glgi_mock.assert_called_once_with(task, pg)
|
||||||
upd_mock.assert_has_calls(
|
upd_mock.assert_has_calls(
|
||||||
[mock.call(self.port.extra['vif_port_id'], call1_body),
|
[mock.call(self.port.extra['vif_port_id'], call1_body),
|
||||||
mock.call(pg.extra['vif_port_id'], call2_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