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:
Kent Wu 2017-10-16 21:30:08 -07:00
parent 8a2b664a49
commit 624d2929f8
2 changed files with 117 additions and 9 deletions

View File

@ -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

View File

@ -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)