Multiple host links support
If there are multiple host links on a compute host, current code would just add the static path of the first one being found to the EPG. This patch provides a way for user to speficy which host link(s) will be associated with the EPG. User has to use this aimctl CLI command to configure the mapping between the physnet and interface on a specific compute host first: aimctl manager host-link-network-label-create <host_name> <network_label> <interface_name> And here is one example: aimctl manager host-link-network-label-create h1.example.com physnet1 eth1 Then during the port binding phase, we will use the compute host + network physnet to find the corresponding interface first. We will then use that interface + the compute host to look up the specific host link(s) to use. Change-Id: Ia7de6d7f904a0507b5b206769c1b691de3807a3c
This commit is contained in:
parent
8a2b664a49
commit
624d2929f8
|
@ -2670,17 +2670,40 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
if exist:
|
||||
return
|
||||
|
||||
static_path_updated = False
|
||||
aim_ctx = aim_context.AimContext(db_session=session)
|
||||
host_link = self.aim.find(aim_ctx, aim_infra.HostLink, host_name=host)
|
||||
if not host_link or not host_link[0].path:
|
||||
LOG.warning(_LW('No host link information found for host %s'),
|
||||
host)
|
||||
return
|
||||
host_link = host_link[0].path
|
||||
host_link_net_labels = self.aim.find(
|
||||
aim_ctx, aim_infra.HostLinkNetworkLabel, host_name=host,
|
||||
network_label=segment[api.PHYSICAL_NETWORK])
|
||||
if host_link_net_labels:
|
||||
for hl_net_label in host_link_net_labels:
|
||||
interface = hl_net_label.interface_name
|
||||
host_link = self.aim.find(
|
||||
aim_ctx, aim_infra.HostLink, host_name=host,
|
||||
interface_name=interface)
|
||||
if not host_link or not host_link[0].path:
|
||||
LOG.warning(_LW('No host link information found for host: '
|
||||
'%(host)s, interface: %(interface)s'),
|
||||
{'host': host, 'interface': interface})
|
||||
continue
|
||||
host_link = host_link[0].path
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
static_path_updated = True
|
||||
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
# acting as a fallback also
|
||||
if not static_path_updated:
|
||||
host_link = self.aim.find(aim_ctx, aim_infra.HostLink,
|
||||
host_name=host)
|
||||
if not host_link or not host_link[0].path:
|
||||
LOG.warning(_LW('No host link information found for host %s'),
|
||||
host)
|
||||
return
|
||||
host_link = host_link[0].path
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
|
||||
def _release_dynamic_segment(self, port_context, use_original=False):
|
||||
top = (port_context.original_top_bound_segment if use_original
|
||||
|
|
|
@ -43,6 +43,7 @@ from neutron.tests.unit.extensions import test_securitygroup
|
|||
from neutron_lib import constants as n_constants
|
||||
from neutron_lib.plugins import directory
|
||||
from opflexagent import constants as ofcst
|
||||
from oslo_config import cfg
|
||||
import webob.exc
|
||||
|
||||
from gbpservice.neutron.db import implicitsubnetpool_db # noqa
|
||||
|
@ -5043,6 +5044,90 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
|||
[{'path': self.hlink1.path, 'encap': 'vlan-%s' % vlan_p1}],
|
||||
epg1.static_paths)
|
||||
|
||||
def test_ports_with_2_hostlinks(self):
|
||||
aim_ctx = aim_context.AimContext(self.db_session)
|
||||
hlink_1a = aim_infra.HostLink(
|
||||
host_name='h1',
|
||||
interface_name='eth1',
|
||||
path='topology/pod-1/paths-102/pathep-[eth1/8]')
|
||||
hlink_1b = aim_infra.HostLink(
|
||||
host_name='h1',
|
||||
interface_name='eth2',
|
||||
path='topology/pod-1/paths-102/pathep-[eth1/9]')
|
||||
self.aim_mgr.create(aim_ctx, hlink_1a)
|
||||
self.aim_mgr.create(aim_ctx, hlink_1b)
|
||||
hlink_net_lable1 = aim_infra.HostLinkNetworkLabel(
|
||||
host_name='h1', network_label='physnet3',
|
||||
interface_name='eth1')
|
||||
hlink_net_lable2 = aim_infra.HostLinkNetworkLabel(
|
||||
host_name='h1', network_label='physnet3',
|
||||
interface_name='eth2')
|
||||
hlink_net_lable3 = aim_infra.HostLinkNetworkLabel(
|
||||
host_name='h1', network_label='physnet2',
|
||||
interface_name='eth999')
|
||||
self.aim_mgr.create(aim_ctx, hlink_net_lable1)
|
||||
self.aim_mgr.create(aim_ctx, hlink_net_lable2)
|
||||
self.aim_mgr.create(aim_ctx, hlink_net_lable3)
|
||||
|
||||
net_type = cfg.CONF.ml2.tenant_network_types[0]
|
||||
net1 = self._make_network(
|
||||
self.fmt, 'net1', True,
|
||||
arg_list=('provider:physical_network', 'provider:network_type'),
|
||||
**{'provider:physical_network': 'physnet3',
|
||||
'provider:network_type': net_type})['network']
|
||||
epg1 = self._net_2_epg(net1)
|
||||
|
||||
with self.subnet(network={'network': net1}) as sub1:
|
||||
with self.port(subnet=sub1) as p1:
|
||||
# bind p1 to host h1
|
||||
p1 = self._bind_port_to_host(p1['port']['id'], 'h1')
|
||||
vlan_p1 = self._check_binding(p1['port']['id'])
|
||||
|
||||
epg1 = self.aim_mgr.get(aim_ctx, epg1)
|
||||
self.assertEqual(
|
||||
[{'path': hlink_1a.path, 'encap': 'vlan-%s' % vlan_p1},
|
||||
{'path': hlink_1b.path, 'encap': 'vlan-%s' % vlan_p1}],
|
||||
epg1.static_paths)
|
||||
|
||||
# test the fallback
|
||||
net2 = self._make_network(
|
||||
self.fmt, 'net2', True,
|
||||
arg_list=('provider:physical_network', 'provider:network_type'),
|
||||
**{'provider:physical_network': 'physnet2',
|
||||
'provider:network_type': net_type})['network']
|
||||
epg2 = self._net_2_epg(net2)
|
||||
|
||||
with self.subnet(network={'network': net2}) as sub2:
|
||||
with self.port(subnet=sub2) as p2:
|
||||
# bind p2 to host h1
|
||||
p2 = self._bind_port_to_host(p2['port']['id'], 'h1')
|
||||
vlan_p2 = self._check_binding(p2['port']['id'])
|
||||
|
||||
self.assertNotEqual(vlan_p1, vlan_p2)
|
||||
|
||||
host_links = self.aim_mgr.find(aim_ctx, aim_infra.HostLink,
|
||||
host_name='h1')
|
||||
epg2 = self.aim_mgr.get(aim_ctx, epg2)
|
||||
self.assertEqual(
|
||||
[{'path': host_links[0].path, 'encap': 'vlan-%s' % vlan_p2}],
|
||||
epg2.static_paths)
|
||||
|
||||
self._delete('ports', p2['port']['id'])
|
||||
epg2 = self.aim_mgr.get(aim_ctx, epg2)
|
||||
self._check_no_dynamic_segment(net2['id'])
|
||||
self.assertEqual([], epg2.static_paths)
|
||||
|
||||
epg1 = self.aim_mgr.get(aim_ctx, epg1)
|
||||
self.assertEqual(
|
||||
[{'path': hlink_1a.path, 'encap': 'vlan-%s' % vlan_p1},
|
||||
{'path': hlink_1b.path, 'encap': 'vlan-%s' % vlan_p1}],
|
||||
epg1.static_paths)
|
||||
|
||||
self._delete('ports', p1['port']['id'])
|
||||
epg1 = self.aim_mgr.get(aim_ctx, epg1)
|
||||
self._check_no_dynamic_segment(net1['id'])
|
||||
self.assertEqual([], epg1.static_paths)
|
||||
|
||||
def test_network_on_multiple_hosts(self):
|
||||
aim_ctx = aim_context.AimContext(self.db_session)
|
||||
|
||||
|
|
Loading…
Reference in New Issue