create Vxlan tunnels from ESXi compute node

networking-vsphere, which works for ESXi nodes
does not support l2 population. so choosing
ovs-vapp agent instead of ovs agent to create tunnel
by tunnel sync call, while creating l2-gateway-connection.

Closes-Bug: 1544112

Change-Id: I801e539ccc4f9bdce3b829189c97c53ba84a8f48
changes/16/292816/15
vikas 7 years ago
parent 48df6c1dc2
commit b0b5431d9c

@ -0,0 +1,51 @@
# Copyright (c) 2016 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from neutron.common import topics as neutron_topics
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from neutron.plugins.ml2 import managers
from neutron.plugins.ml2 import rpc as rpc
class Tunnel_Calls(object):
"""Common tunnel calls for L2 agent."""
def __init__(self):
self._construct_rpc_stuff()
def _construct_rpc_stuff(self):
self.notifier = rpc.AgentNotifierApi(neutron_topics.AGENT)
self.type_manager = managers.TypeManager()
self.tunnel_rpc_obj = rpc.RpcCallbacks(self.notifier,
self.type_manager)
def trigger_tunnel_sync(self, context, tunnel_ip):
"""Sends tunnel sync RPC message to the neutron
L2 agent.
"""
tunnel_dict = {'tunnel_ip': tunnel_ip,
'tunnel_type': 'vxlan'}
self.tunnel_rpc_obj.tunnel_sync(context,
**tunnel_dict)
def trigger_l2pop_sync(self, context, other_fdb_entries):
"""Sends L2pop ADD RPC message to the neutron L2 agent."""
l2pop_rpc.L2populationAgentNotifyAPI(
).add_fdb_entries(context, other_fdb_entries)
def trigger_l2pop_delete(self, context, other_fdb_entries, host=None):
"""Sends L2pop DELETE RPC message to the neutron L2 agent."""
l2pop_rpc.L2populationAgentNotifyAPI(
).remove_fdb_entries(context, other_fdb_entries, host)

@ -28,15 +28,15 @@ class L2GatewayNotFound(exceptions.NotFound):
message = _("L2 Gateway %(gateway_id)s could not be found")
class OvsAgentNotFound(exceptions.NotFound):
message = _("ovs agent not found in host %(host)s")
class L2GatewayDeviceInUse(exceptions.InUse):
message = _("L2 Gateway Device '%(device_id)s' is still used by "
"one or more network gateways.")
class L2AgentNotFoundByHost(exceptions.NotFound):
message = _("L2 Agent for host '%(host)s' could not be found.")
class L2GatewayDeviceNotFound(exceptions.NotFound):
message = _("L2 Gateway Device %(device_id)s could not be found.")
@ -129,5 +129,5 @@ base.FAULT_MAP.update({L2GatewayInUse: web_exc.HTTPConflict,
L2GatewaySegmentationRequired: web_exc.HTTPConflict,
L2MultipleGatewayConnections: web_exc.HTTPConflict,
L2GatewayDuplicateSegmentationID: web_exc.HTTPConflict,
OvsAgentNotFound: web_exc.HTTPNotFound,
L2AgentNotFoundByHost: web_exc.HTTPNotFound,
OVSDBError: web_exc.HTTPConflict})

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from neutron.extensions import portbindings
from neutron import manager
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from networking_l2gw._i18n import _LE
from networking_l2gw.db.l2gateway import l2gateway_db
@ -22,6 +22,7 @@ from networking_l2gw.db.l2gateway.ovsdb import lib as db
from networking_l2gw.services.l2gateway.common import constants as n_const
from networking_l2gw.services.l2gateway.common import ovsdb_schema
from networking_l2gw.services.l2gateway.common import topics
from networking_l2gw.services.l2gateway.common import tunnel_calls
from networking_l2gw.services.l2gateway import exceptions as l2gw_exc
from networking_l2gw.services.l2gateway.service_drivers import agent_api
@ -70,6 +71,8 @@ class OVSDBData(object):
self.agent_rpc = agent_api.L2gatewayAgentApi(
topics.L2GATEWAY_AGENT, cfg.CONF.host)
self.l2gw_mixin = l2gateway_db.L2GatewayMixin()
self.core_plugin = manager.NeutronManager.get_plugin()
self.tunnel_call = tunnel_calls.Tunnel_Calls()
def update_ovsdb_changes(self, context, ovsdb_data):
"""RPC to update the changes from OVSDB in the database."""
@ -266,13 +269,55 @@ class OVSDBData(object):
physical_switch_ips.add(physical_switch.get('tunnel_ip'))
return list(physical_switch_ips)
def _get_agent_by_mac(self, context, mac):
host = None
mac_addr = mac.get('mac')
port = self._get_port_by_mac(context, mac_addr)
for port_dict in port:
host = port_dict[portbindings.HOST_ID]
agent_l2_pop_enabled = self._get_agent_details_by_host(context, host)
return agent_l2_pop_enabled
def _get_port_by_mac(self, context, mac_addr):
port = self.core_plugin.get_ports(
context, filters={'mac_address': [mac_addr]})
return port
def _get_agent_details_by_host(self, context, host):
l2_agent = None
agent_l2_pop_enabled = None
agents = self.core_plugin.get_agents(
context, filters={'host': [host]})
for agent in agents:
agent_tunnel_type = agent['configurations'].get('tunnel_types', [])
agent_l2_pop_enabled = agent['configurations'].get('l2_population',
None)
if n_const.VXLAN in agent_tunnel_type:
l2_agent = agent
break
if not l2_agent:
raise l2gw_exc.L2AgentNotFoundByHost(
host=host)
return agent_l2_pop_enabled
def _handle_l2pop(self, context, new_remote_macs):
"""handle vxlan tunnel creation based on whether l2pop is enabled or not.
if l2pop is enabled in L2 agent on a host to which port belongs, then
call add_fdb_entries. otherwise, call tunnel_sync.
"""
for mac in new_remote_macs:
agent_ips = self._get_physical_switch_ips(context, mac)
for agent_ip in agent_ips:
agent_l2_pop_enabled = self._get_agent_by_mac(context, mac)
physical_switches = self._get_physical_switch_ips(context, mac)
for physical_switch in physical_switches:
other_fdb_entries = self._get_fdb_entries(
context, agent_ip, mac.get('logical_switch_id'))
self._trigger_l2pop_sync(context, other_fdb_entries)
context, physical_switch, mac.get('logical_switch_id'))
if agent_l2_pop_enabled:
self.tunnel_call.trigger_l2pop_sync(context,
other_fdb_entries)
else:
self.tunnel_call.trigger_tunnel_sync(context,
physical_switch)
def _process_modified_physical_ports(self,
context,
@ -418,13 +463,13 @@ class OVSDBData(object):
other_fdb_entries = self._get_fdb_entries(
context, physical_switch_ip, logical_switch_id)
agent_host = tunneling_ip_dict.get(agent_ip)
self._trigger_l2pop_delete(
self.tunnel_call.trigger_l2pop_delete(
context, other_fdb_entries, agent_host)
else:
for logical_switch_id in logical_switch_ids:
other_fdb_entries = self._get_fdb_entries(
context, agent_ip, logical_switch_id)
self._trigger_l2pop_delete(
self.tunnel_call.trigger_l2pop_delete(
context, other_fdb_entries)
db.delete_physical_locator(context, pl_dict)
@ -454,13 +499,14 @@ class OVSDBData(object):
def _get_agent_ips(self, context):
agent_ip_dict = {}
ml2plugin = manager.NeutronManager.get_plugin()
agents = ml2plugin.get_agents(
context, filters={'agent_type': [constants.AGENT_TYPE_OVS]})
agents = self.core_plugin.get_agents(
context)
for agent in agents:
conf_dict = agent.get('configurations')
tunnel_ip = conf_dict.get('tunneling_ip')
agent_ip_dict[tunnel_ip] = agent.get('host')
conf_dict_tunnel_type = agent['configurations'].get(
"tunnel_types", [])
if n_const.VXLAN in conf_dict_tunnel_type:
tunnel_ip = agent['configurations'].get('tunneling_ip')
agent_ip_dict[tunnel_ip] = agent.get('host')
return agent_ip_dict
def _get_fdb_entries(self, context, agent_ip, logical_switch_uuid):
@ -476,13 +522,3 @@ class OVSDBData(object):
[port_fdb_entries]
}}}
return other_fdb_entries
def _trigger_l2pop_sync(self, context, other_fdb_entries):
"""Sends L2pop ADD RPC message to the neutron L2 agent."""
l2pop_rpc.L2populationAgentNotifyAPI(
).add_fdb_entries(context, other_fdb_entries)
def _trigger_l2pop_delete(self, context, other_fdb_entries, host=None):
"""Sends L2pop DELETE RPC message to the neutron L2 agent."""
l2pop_rpc.L2populationAgentNotifyAPI(
).remove_fdb_entries(context, other_fdb_entries, host)

@ -30,7 +30,6 @@ from networking_l2gw.services.l2gateway import exceptions as l2gw_exc
from networking_l2gw.services.l2gateway import service_drivers
from networking_l2gw.services.l2gateway.service_drivers import agent_api
from neutron_lib import constants as n_const
from neutron_lib import exceptions
from oslo_config import cfg
from oslo_log import log as logging
@ -473,14 +472,11 @@ class L2gwRpcDriver(service_drivers.L2gwDriver):
def _get_ip_details(self, context, port):
host = port[portbindings.HOST_ID]
agent = self._get_agent_details(context, host)
if agent:
conf_dict = agent[0].get("configurations")
dst_ip = conf_dict.get("tunneling_ip")
fixed_ip_list = port.get('fixed_ips')
fixed_ip_list = fixed_ip_list[0]
return dst_ip, fixed_ip_list.get('ip_address')
else:
raise l2gw_exc.OvsAgentNotFound(host=host)
conf_dict = agent.get("configurations")
dst_ip = conf_dict.get("tunneling_ip")
fixed_ip_list = port.get('fixed_ips')
fixed_ip_list = fixed_ip_list[0]
return dst_ip, fixed_ip_list.get('ip_address')
def _get_network_details(self, context, network_id):
network = self.service_plugin._core_plugin.get_network(context,
@ -493,11 +489,18 @@ class L2gwRpcDriver(service_drivers.L2gwDriver):
return ports
def _get_agent_details(self, context, host):
agent = self.service_plugin._core_plugin.get_agents(
context,
filters={'agent_type': [n_const.AGENT_TYPE_OVS],
'host': [host]})
return agent
l2_agent = None
agents = self.service_plugin._core_plugin.get_agents(
context, filters={'host': [host]})
for agent in agents:
agent_tunnel_type = agent['configurations'].get('tunnel_types', [])
if constants.VXLAN in agent_tunnel_type:
l2_agent = agent
break
if not l2_agent:
raise l2gw_exc.L2AgentNotFoundByHost(
host=host)
return l2_agent
def _get_logical_switch_dict(self, context, logical_switch, gw_connection):
if logical_switch:

@ -0,0 +1,62 @@
# Copyright (c) 2016 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import mock
import contextlib
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from neutron.plugins.ml2.drivers import type_tunnel
from neutron.plugins.ml2 import managers
from neutron.plugins.ml2 import rpc as rpc
from neutron.tests import base
from networking_l2gw.services.l2gateway.common import tunnel_calls
class TestTunnelCalls(base.BaseTestCase):
def setUp(self):
super(TestTunnelCalls, self).setUp()
mock.patch.object(managers, 'TypeManager').start()
self.tunnel_call = tunnel_calls.Tunnel_Calls()
self.context = mock.MagicMock()
def test_trigger_tunnel_sync(self):
with contextlib.nested(
mock.patch.object(rpc, 'RpcCallbacks'),
mock.patch.object(type_tunnel.TunnelRpcCallbackMixin,
'tunnel_sync')) as (mock_rpc, mock_tunnel_sync):
self.tunnel_call.trigger_tunnel_sync(self.context, 'fake_ip')
mock_tunnel_sync.assert_called_with(
self.context, tunnel_ip='fake_ip', tunnel_type='vxlan')
def test_trigger_l2pop_sync(self):
fake_fdb_entry = "fake_fdb_entry"
with mock.patch.object(l2pop_rpc.L2populationAgentNotifyAPI,
'add_fdb_entries') as (mock_add_fdb):
self.tunnel_call.trigger_l2pop_sync(self.context,
fake_fdb_entry)
mock_add_fdb.assert_called_with(self.context,
fake_fdb_entry)
def test_trigger_l2pop_delete(self):
fake_fdb_entry = "fake_fdb_entry"
fake_host = 'fake_host'
with mock.patch.object(l2pop_rpc.L2populationAgentNotifyAPI,
'remove_fdb_entries') as (mock_delete_fdb):
self.tunnel_call.trigger_l2pop_delete(self.context,
fake_fdb_entry, fake_host)
mock_delete_fdb.assert_called_with(self.context,
fake_fdb_entry, fake_host)

@ -18,12 +18,14 @@ import mock
import contextlib
from neutron import context
from neutron import manager
from neutron.plugins.ml2 import managers
from neutron.tests import base
from networking_l2gw.db.l2gateway import l2gateway_db
from networking_l2gw.db.l2gateway.ovsdb import lib
from networking_l2gw.services.l2gateway.common import constants as n_const
from networking_l2gw.services.l2gateway.common import ovsdb_schema
from networking_l2gw.services.l2gateway.common import tunnel_calls
from networking_l2gw.services.l2gateway.ovsdb import data
from networking_l2gw.services.l2gateway.service_drivers import agent_api
@ -67,6 +69,8 @@ class TestOVSDBData(base.BaseTestCase):
super(TestOVSDBData, self).setUp()
self.context = context.get_admin_context()
self.ovsdb_identifier = 'fake_ovsdb_id'
mock.patch.object(manager.NeutronManager, 'get_plugin').start()
mock.patch.object(managers, 'TypeManager').start()
self.ovsdb_data = data.OVSDBData(self.ovsdb_identifier)
def test_init(self):
@ -144,7 +148,9 @@ class TestOVSDBData(base.BaseTestCase):
mock.patch.object(self.ovsdb_data,
'_process_deleted_local_macs'),
mock.patch.object(self.ovsdb_data,
'_process_deleted_remote_macs')
'_process_deleted_remote_macs'),
mock.patch.object(self.ovsdb_data,
'_handle_l2pop')
) as (process_new_logical_switches,
process_new_physical_ports,
process_new_physical_switches,
@ -158,7 +164,8 @@ class TestOVSDBData(base.BaseTestCase):
process_deleted_physical_ports,
process_deleted_physical_locators,
process_deleted_local_macs,
process_deleted_remote_macs):
process_deleted_remote_macs,
mock_handle_l2pop):
self.ovsdb_data.entry_table = {
'new_logical_switches': process_new_logical_switches,
'new_physical_ports': process_new_physical_ports,
@ -205,6 +212,7 @@ class TestOVSDBData(base.BaseTestCase):
self.context, fake_deleted_local_macs)
process_deleted_remote_macs.assert_called_with(
self.context, fake_deleted_remote_macs)
self.assertTrue(mock_handle_l2pop.called)
def test_notify_ovsdb_states(self):
fake_ovsdb_states = {'ovsdb1': 'connected'}
@ -377,6 +385,31 @@ class TestOVSDBData(base.BaseTestCase):
'fake_ovsdb_id')
delete_ls.assert_called_with(self.context, fake_dict)
def test_get_agent_by_mac(self):
fake_mac = {'mac': 'fake_mac_1'}
fake_port = [{'binding:host_id': 'fake_host'}]
with contextlib.nested(
mock.patch.object(self.ovsdb_data, '_get_port_by_mac',
return_value=fake_port),
mock.patch.object(self.ovsdb_data,
'_get_agent_details_by_host')) as (
mock_get_port_mac, mock_get_agent_detail):
self.ovsdb_data._get_agent_by_mac(self.context, fake_mac)
mock_get_port_mac.assert_called_with(self.context, 'fake_mac_1')
mock_get_agent_detail.assert_called_with(self.context, 'fake_host')
def test_get_agent_details_by_host(self):
fake_agent = {'configurations': {'tunnel_types': ["vxlan"],
'l2_population': True}}
fake_agents = [fake_agent]
with contextlib.nested(
mock.patch.object(self.ovsdb_data.core_plugin,
'get_agents',
return_value=fake_agents)):
l2pop_enabled = self.ovsdb_data._get_agent_details_by_host(
self.context, 'fake_host')
self.assertTrue(l2pop_enabled)
def test_process_deleted_physical_switches(self):
fake_dict = {}
fake_deleted_physical_switches = [fake_dict]
@ -509,7 +542,8 @@ class TestOVSDBData(base.BaseTestCase):
'delete_physical_locator'),
mock.patch.object(data.OVSDBData, '_get_agent_ips',
return_value={'1.1.1.1': 'hostname'}),
mock.patch.object(data.OVSDBData, '_trigger_l2pop_delete')
mock.patch.object(tunnel_calls.Tunnel_Calls,
'trigger_l2pop_delete')
) as (get_ls, get_all_ps, get_fdb, delete_pl, get_agent_ips,
trig_l2pop):
self.ovsdb_data._process_deleted_physical_locators(
@ -547,7 +581,8 @@ class TestOVSDBData(base.BaseTestCase):
'delete_physical_locator'),
mock.patch.object(data.OVSDBData, '_get_agent_ips',
return_value={'2.2.2.2': 'hostname'}),
mock.patch.object(data.OVSDBData, '_trigger_l2pop_delete')
mock.patch.object(tunnel_calls.Tunnel_Calls,
'trigger_l2pop_delete')
) as (get_ls, get_all_ps, get_fdb, delete_pl, get_agent_ips,
trig_l2pop):
self.ovsdb_data._process_deleted_physical_locators(

@ -42,7 +42,6 @@ class TestL2gwRpcDriver(base.BaseTestCase):
self.plugin = rpc_l2gw.L2gwRpcDriver(self.service_plugin)
self.plugin.agent_rpc = mock.MagicMock()
self.ovsdb_identifier = 'fake_ovsdb_id'
self.ovsdb_data = data.OVSDBData(self.ovsdb_identifier)
self.context = mock.ANY
def test_l2rpcdriver_init(self):
@ -262,7 +261,7 @@ class TestL2gwRpcDriver(base.BaseTestCase):
def test_get_ip_details(self):
fake_port = {'binding:host_id': 'fake_host',
'fixed_ips': [{'ip_address': 'fake_ip'}]}
fake_agent = [{'configurations': {'tunneling_ip': 'fake_tun_ip'}}]
fake_agent = {'configurations': {'tunneling_ip': 'fake_tun_ip'}}
with mock.patch.object(self.plugin,
'_get_agent_details',
return_value=fake_agent) as get_agent:
@ -272,16 +271,14 @@ class TestL2gwRpcDriver(base.BaseTestCase):
self.assertEqual(ret_dst_ip, 'fake_tun_ip')
self.assertEqual(ret_ip_add, 'fake_ip')
def test_get_ip_details_for_no_ovs_agent(self):
fake_port = {'binding:host_id': 'fake_host',
'fixed_ips': [{'ip_address': 'fake_ip'}]}
with mock.patch.object(self.plugin,
'_get_agent_details',
return_value=None):
self.assertRaises(l2gw_exc.OvsAgentNotFound,
self.plugin._get_ip_details,
self.context,
fake_port)
def test_get_agent_details_for_no_ovs_agent(self):
core_plugin = mock.PropertyMock()
type(self.service_plugin)._core_plugin = core_plugin
(self.service_plugin._core_plugin.get_agents.
return_value) = []
self.assertRaises(l2gw_exc.L2AgentNotFoundByHost,
self.plugin._get_agent_details,
self.context, 'fake_host')
def test_get_network_details(self):
fake_network = {'id': 'fake_network_id',

@ -19,7 +19,6 @@ from neutron.tests import base
from networking_l2gw.db.l2gateway import l2gateway_db
from networking_l2gw.services.l2gateway.common import config
from networking_l2gw.services.l2gateway.ovsdb import data
from networking_l2gw.services.l2gateway import plugin as l2gw_plugin
@ -39,7 +38,6 @@ class TestL2GatewayPlugin(base.BaseTestCase):
self.context = mock.MagicMock()
self.plugin = l2gw_plugin.L2GatewayPlugin()
self.ovsdb_identifier = 'fake_ovsdb_id'
self.ovsdb_data = data.OVSDBData(self.ovsdb_identifier)
def _get_fake_l2_gateway(self):
fake_l2_gateway_id = "5227c228-6bba-4bbe-bdb8-6942768ff0f1"

Loading…
Cancel
Save