Merge "Change provider network segmentation ID in OVS agent"
This commit is contained in:
commit
8887343782
@ -112,6 +112,7 @@ class PluginApi(object):
|
||||
1.4 - tunnel_sync rpc signature upgrade to obtain 'host'
|
||||
1.5 - Support update_device_list and
|
||||
get_devices_details_list_and_failed_devices
|
||||
1.6 - Support get_network_details
|
||||
'''
|
||||
|
||||
def __init__(self, topic):
|
||||
@ -142,6 +143,11 @@ class PluginApi(object):
|
||||
'get_devices_details_list_and_failed_devices',
|
||||
devices=devices, agent_id=agent_id, host=host)
|
||||
|
||||
def get_network_details(self, context, network, agent_id, host=None):
|
||||
cctxt = self.client.prepare(version='1.6')
|
||||
return cctxt.call(context, 'get_network_details', network=network,
|
||||
agent_id=agent_id, host=host)
|
||||
|
||||
def update_device_down(self, context, device, agent_id, host=None):
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'update_device_down', device=device,
|
||||
|
@ -25,6 +25,7 @@ import netaddr
|
||||
from neutron_lib.agent import constants as agent_consts
|
||||
from neutron_lib.agent import topics
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from neutron_lib.callbacks import events as callback_events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources as callback_resources
|
||||
@ -400,6 +401,37 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||
n_const.TYPE_GRE: {},
|
||||
n_const.TYPE_VXLAN: {}}
|
||||
|
||||
def _update_network_segmentation_id(self, network):
|
||||
if network[provider_net.NETWORK_TYPE] != n_const.TYPE_VLAN:
|
||||
return
|
||||
|
||||
try:
|
||||
lvm = self.vlan_manager.get(network['id'])
|
||||
except vlanmanager.MappingNotFound:
|
||||
return
|
||||
|
||||
segmentation_id_old = lvm.segmentation_id
|
||||
if segmentation_id_old == network[provider_net.SEGMENTATION_ID]:
|
||||
return
|
||||
self.vlan_manager.update_segmentation_id(
|
||||
network['id'], network[provider_net.SEGMENTATION_ID])
|
||||
|
||||
lvid = lvm.vlan
|
||||
physical_network = network[provider_net.PHYSICAL_NETWORK]
|
||||
phys_br = self.phys_brs[physical_network]
|
||||
phys_port = self.phys_ofports[physical_network]
|
||||
int_port = self.int_ofports[physical_network]
|
||||
phys_br.reclaim_local_vlan(port=phys_port, lvid=lvid)
|
||||
phys_br.provision_local_vlan(
|
||||
port=phys_port, lvid=lvid,
|
||||
segmentation_id=network[provider_net.SEGMENTATION_ID],
|
||||
distributed=self.enable_distributed_routing)
|
||||
self.int_br.reclaim_local_vlan(port=int_port,
|
||||
segmentation_id=segmentation_id_old)
|
||||
self.int_br.provision_local_vlan(
|
||||
port=int_port, lvid=lvid,
|
||||
segmentation_id=network[provider_net.SEGMENTATION_ID])
|
||||
|
||||
def setup_rpc(self):
|
||||
self.plugin_rpc = OVSPluginApi(topics.PLUGIN)
|
||||
# allow us to receive port_update/delete callbacks from the cache
|
||||
@ -449,6 +481,9 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||
|
||||
def network_update(self, context, **kwargs):
|
||||
network_id = kwargs['network']['id']
|
||||
network = self.plugin_rpc.get_network_details(
|
||||
self.context, network_id, self.agent_id, self.conf.host)
|
||||
self._update_network_segmentation_id(network)
|
||||
for port_id in self.network_ports[network_id]:
|
||||
# notifications could arrive out of order, if the port is deleted
|
||||
# we don't want to update it anymore
|
||||
|
@ -108,3 +108,9 @@ class LocalVlanManager(object):
|
||||
return self.mapping.pop(net_id)
|
||||
except KeyError:
|
||||
raise MappingNotFound(net_id=net_id)
|
||||
|
||||
def update_segmentation_id(self, net_id, segmentation_id):
|
||||
try:
|
||||
self.mapping[net_id].segmentation_id = segmentation_id
|
||||
except KeyError:
|
||||
raise MappingNotFound(net_id=net_id)
|
||||
|
@ -17,6 +17,7 @@ import os
|
||||
import uuid
|
||||
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib import constants
|
||||
@ -200,3 +201,7 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
a_const.VHOST_USER_SOCKET_DIR)
|
||||
sock_name = (constants.VHOST_USER_DEVICE_PREFIX + port_id)[:14]
|
||||
return os.path.join(sockdir, sock_name)
|
||||
|
||||
@staticmethod
|
||||
def provider_network_attribute_updates_supported():
|
||||
return [provider_net.SEGMENTATION_ID]
|
||||
|
@ -51,7 +51,8 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
|
||||
# 1.4 tunnel_sync rpc signature upgrade to obtain 'host'
|
||||
# 1.5 Support update_device_list and
|
||||
# get_devices_details_list_and_failed_devices
|
||||
target = oslo_messaging.Target(version='1.5')
|
||||
# 1.6 Support get_network_details
|
||||
target = oslo_messaging.Target(version='1.6')
|
||||
|
||||
def __init__(self, notifier, type_manager):
|
||||
self.setup_tunnel_callback_mixin(notifier, type_manager)
|
||||
@ -69,7 +70,7 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
|
||||
def _get_request_details(kwargs):
|
||||
return (kwargs.get('agent_id'),
|
||||
kwargs.get('host'),
|
||||
kwargs.get('device'))
|
||||
kwargs.get('device') or kwargs.get('network'))
|
||||
|
||||
def get_device_details(self, rpc_context, **kwargs):
|
||||
"""Agent requests device details."""
|
||||
@ -219,6 +220,15 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
|
||||
return {'devices': devices,
|
||||
'failed_devices': failed_devices}
|
||||
|
||||
def get_network_details(self, rpc_context, **kwargs):
|
||||
"""Agent requests network details."""
|
||||
agent_id, host, network = self._get_request_details(kwargs)
|
||||
LOG.debug("Network %(network)s details requested by agent "
|
||||
"%(agent_id)s with host %(host)s",
|
||||
{'network': network, 'agent_id': agent_id, 'host': host})
|
||||
plugin = directory.get_plugin()
|
||||
return plugin.get_network(rpc_context, network)
|
||||
|
||||
def update_device_down(self, rpc_context, **kwargs):
|
||||
"""Device no longer exists on agent."""
|
||||
# TODO(garyk) - live migration and port status
|
||||
|
@ -54,6 +54,9 @@ class AgentRPCPluginApi(base.BaseTestCase):
|
||||
def test_get_devices_details_list(self):
|
||||
self._test_rpc_call('get_devices_details_list')
|
||||
|
||||
def test_get_network_details(self):
|
||||
self._test_rpc_call('get_network_details')
|
||||
|
||||
def test_update_device_down(self):
|
||||
self._test_rpc_call('update_device_down')
|
||||
|
||||
|
@ -17,6 +17,7 @@ import time
|
||||
|
||||
import mock
|
||||
from neutron_lib.agent import constants as agent_consts
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib import rpc as n_rpc
|
||||
from oslo_config import cfg
|
||||
@ -1208,7 +1209,10 @@ class TestOvsNeutronAgent(object):
|
||||
port = {'id': TEST_PORT_ID1, 'network_id': network['id']}
|
||||
|
||||
self.agent._update_port_network(port['id'], port['network_id'])
|
||||
self.agent.network_update(context=None, network=network)
|
||||
with mock.patch.object(self.agent.plugin_rpc, 'get_network_details'), \
|
||||
mock.patch.object(self.agent,
|
||||
'_update_network_segmentation_id'):
|
||||
self.agent.network_update(context=None, network=network)
|
||||
self.assertEqual(set([port['id']]), self.agent.updated_ports)
|
||||
|
||||
def test_network_update_outoforder(self):
|
||||
@ -1222,7 +1226,10 @@ class TestOvsNeutronAgent(object):
|
||||
|
||||
self.agent._update_port_network(port['id'], port['network_id'])
|
||||
self.agent.port_delete(context=None, port_id=port['id'])
|
||||
self.agent.network_update(context=None, network=network)
|
||||
with mock.patch.object(self.agent.plugin_rpc, 'get_network_details'), \
|
||||
mock.patch.object(self.agent,
|
||||
'_update_network_segmentation_id'):
|
||||
self.agent.network_update(context=None, network=network)
|
||||
self.assertEqual(set(), self.agent.updated_ports)
|
||||
|
||||
def test_update_port_network(self):
|
||||
@ -2362,6 +2369,58 @@ class TestOvsNeutronAgent(object):
|
||||
else:
|
||||
bridge.set_datapath_id.assert_called_once_with(dpid)
|
||||
|
||||
def test__update_network_segmentation_id(self):
|
||||
network = {'id': 'my-net-uuid',
|
||||
provider_net.SEGMENTATION_ID: 1005,
|
||||
provider_net.PHYSICAL_NETWORK: 'provider_net',
|
||||
provider_net.NETWORK_TYPE: n_const.TYPE_VLAN}
|
||||
self.agent.vlan_manager.add('my-net-uuid', 5, n_const.TYPE_VLAN,
|
||||
'provider_net', 1004, None)
|
||||
mock_phys_br = mock.Mock()
|
||||
self.agent.phys_brs['provider_net'] = mock_phys_br
|
||||
self.agent.phys_ofports['provider_net'] = 'phy_ofport'
|
||||
self.agent.int_ofports['provider_net'] = 'int_ofport'
|
||||
|
||||
with mock.patch.object(self.agent.int_br, 'reclaim_local_vlan') \
|
||||
as mock_reclaim_local_vlan, \
|
||||
mock.patch.object(self.agent.int_br, 'provision_local_vlan') \
|
||||
as mock_provision_local_vlan:
|
||||
self.agent._update_network_segmentation_id(network)
|
||||
mock_reclaim_local_vlan.assert_called_once_with(
|
||||
port='int_ofport', segmentation_id=1004)
|
||||
mock_provision_local_vlan.assert_called_once_with(
|
||||
port='int_ofport', lvid=5, segmentation_id=1005)
|
||||
mock_phys_br.reclaim_local_vlan.assert_called_once_with(
|
||||
port='phy_ofport', lvid=5)
|
||||
|
||||
def test__update_network_segmentation_id_not_vlan(self):
|
||||
network = {provider_net.NETWORK_TYPE: 'not_vlan'}
|
||||
with mock.patch.object(self.agent.vlan_manager, 'get') as mock_get:
|
||||
self.agent._update_network_segmentation_id(network)
|
||||
mock_get.assert_not_called()
|
||||
|
||||
def test__update_network_segmentation_id_vlan_not_found(self):
|
||||
network = {'id': 'my-net-uuid',
|
||||
provider_net.SEGMENTATION_ID: 1005,
|
||||
provider_net.NETWORK_TYPE: n_const.TYPE_VLAN,
|
||||
provider_net.PHYSICAL_NETWORK: 'default_network'}
|
||||
with mock.patch.object(self.agent.vlan_manager,
|
||||
'update_segmentation_id') as mock_update_segid:
|
||||
self.agent._update_network_segmentation_id(network)
|
||||
mock_update_segid.assert_not_called()
|
||||
|
||||
def test__update_network_segmentation_id_segmentation_id_not_updated(self):
|
||||
network = {'id': 'my-net-uuid',
|
||||
provider_net.SEGMENTATION_ID: 1005,
|
||||
provider_net.NETWORK_TYPE: n_const.TYPE_VLAN,
|
||||
provider_net.PHYSICAL_NETWORK: 'default_network'}
|
||||
self.agent.vlan_manager.add('my-net-uuid', 5, n_const.TYPE_VLAN,
|
||||
'provider_net', 1005, None)
|
||||
with mock.patch.object(self.agent.vlan_manager,
|
||||
'update_segmentation_id') as mock_update_segid:
|
||||
self.agent._update_network_segmentation_id(network)
|
||||
mock_update_segid.assert_not_called()
|
||||
|
||||
|
||||
class TestOvsNeutronAgentOFCtl(TestOvsNeutronAgent,
|
||||
ovs_test_base.OVSOFCtlTestBase):
|
||||
|
@ -118,3 +118,10 @@ class TestLocalVlanManager(base.BaseTestCase):
|
||||
def test_pop_non_existing_raises_exception(self):
|
||||
with testtools.ExpectedException(vlanmanager.MappingNotFound):
|
||||
self.vlan_manager.pop(1)
|
||||
|
||||
def test_update_segmentation_id(self):
|
||||
self.vlan_manager.add('net_id', 'vlan_id', 'vlan', 'phys_net',
|
||||
1001, None)
|
||||
self.assertEqual(1001, self.vlan_manager.get('net_id').segmentation_id)
|
||||
self.vlan_manager.update_segmentation_id('net_id', 1002)
|
||||
self.assertEqual(1002, self.vlan_manager.get('net_id').segmentation_id)
|
||||
|
@ -214,6 +214,17 @@ class RpcCallbacksTestCase(base.BaseTestCase):
|
||||
self.callbacks.get_devices_details_list_and_failed_devices)
|
||||
self._test_get_devices_list(callback, devices, expected)
|
||||
|
||||
def test_get_network_details(self):
|
||||
kwargs = {'agent_id': 'agent_id',
|
||||
'host': 'host_id',
|
||||
'network': 'network'}
|
||||
with mock.patch.object(self.plugin, 'get_network') as mock_get_network:
|
||||
mock_get_network.return_value = 'net_details'
|
||||
self.assertEqual(
|
||||
'net_details',
|
||||
self.callbacks.get_network_details('fake_context', **kwargs))
|
||||
mock_get_network.assert_called_once_with('fake_context', 'network')
|
||||
|
||||
def test_get_devices_details_list_and_failed_devices_empty_dev(self):
|
||||
with mock.patch.object(self.callbacks, 'get_device_details') as f:
|
||||
res = self.callbacks.get_devices_details_list_and_failed_devices(
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The segmentation ID of a provider network can be now modified, even with
|
||||
OVS ports bound. Note that, during this process, the traffic of the bound
|
||||
ports tagged with the former segmentation ID (external VLAN) will be mapped
|
||||
to the new one. This can provoke a traffic disruption while the external
|
||||
network VLAN is migrated to the new tag.
|
Loading…
Reference in New Issue
Block a user