Nicira NVP plugin support for l3_ext_gw_mode extension

Bug 1121129

This patch adds support the 'configurable external gateway' extension
in the NVP plugin.

Change-Id: I531ebe0053b1b9e21d6f0685776acebe3173b170
This commit is contained in:
Salvatore 2013-04-04 12:35:50 +02:00 committed by Salvatore Orlando
parent 69ebd0870a
commit 60a392f0aa
5 changed files with 74 additions and 22 deletions

View File

@ -43,7 +43,9 @@ class L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
'enable_snat': router.enable_snat}
return self._fields(res, fields)
def _update_router_gw_info(self, context, router_id, info):
def _update_router_gw_info(self, context, router_id, info, router=None):
# Load the router only if necessary
if not router:
router = self._get_router(context, router_id)
# if enable_snat is not specified use the value
# stored in the database (default:True)
@ -54,6 +56,9 @@ class L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
# Calls superclass, pass router db object for avoiding re-loading
super(L3_NAT_db_mixin, self)._update_router_gw_info(
context, router_id, info, router=router)
# Returning the router might come back useful if this
# method is overriden in child classes
return router
def _build_routers_list(self, routers, gw_ports):
gw_port_id_gw_port_dict = {}

View File

@ -34,6 +34,7 @@ migration_for_plugins = [
'neutron.plugins.linuxbridge.lb_neutron_plugin.LinuxBridgePluginV2',
'neutron.plugins.metaplugin.meta_neutron_plugin.MetaPluginV2',
'neutron.plugins.nec.nec_plugin.NECPluginV2',
'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
'neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2',
'neutron.plugins.ryu.ryu_neutron_plugin.RyuNeutronPluginV2'
]

View File

@ -44,6 +44,7 @@ from neutron.db import db_base_plugin_v2
from neutron.db import dhcp_rpc_base
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import l3_gwmode_db
from neutron.db import models_v2
from neutron.db import portsecurity_db
from neutron.db import quota_db # noqa
@ -73,6 +74,7 @@ from neutron.plugins.nicira import nvplib
LOG = logging.getLogger("NeutronPlugin")
NVP_NOSNAT_RULES_ORDER = 10
NVP_FLOATINGIP_NAT_RULES_ORDER = 224
NVP_EXTGW_NAT_RULES_ORDER = 255
@ -127,6 +129,7 @@ class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
extraroute_db.ExtraRoute_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin,
portsecurity_db.PortSecurityDbMixin,
securitygroups_db.SecurityGroupDbMixin,
mac_db.MacLearningDbMixin,
@ -141,7 +144,8 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
functionality using NVP.
"""
supported_extension_aliases = ["extraroute",
supported_extension_aliases = ["ext_gw_mode",
"extraroute",
"mac-learning",
"network-gateway",
"nvp-qos",
@ -278,6 +282,58 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
attachment_vlan)
return lrouter_port
def _update_router_gw_info(self, context, router_id, info):
# NOTE(salvatore-orlando): We need to worry about rollback of NVP
# configuration in case of failures in the process
# Ref. LP bug 1102301
router = self._get_router(context, router_id)
# Check whether SNAT rule update should be triggered
# NVP also supports multiple external networks so there is also
# the possibility that NAT rules should be replaced
current_ext_net_id = router.gw_port_id and router.gw_port.network_id
new_ext_net_id = info and info.get('network_id')
# SNAT should be enabled unless info['enable_snat'] is
# explicitly set to false
enable_snat = new_ext_net_id and info.get('enable_snat', True)
# Remove if ext net removed, changed, or if snat disabled
remove_snat_rules = (current_ext_net_id and
new_ext_net_id != current_ext_net_id or
router.enable_snat and not enable_snat)
# Add rules if snat is enabled, and if either the external network
# changed or snat was previously disabled
# NOTE: enable_snat == True implies new_ext_net_id != None
add_snat_rules = (enable_snat and
(new_ext_net_id != current_ext_net_id or
not router.enable_snat))
router = super(NvpPluginV2, self)._update_router_gw_info(
context, router_id, info, router=router)
# Add/Remove SNAT rules as needed
# Create an elevated context for dealing with metadata access
# cidrs which are created within admin context
ctx_elevated = context.elevated()
if remove_snat_rules or add_snat_rules:
cidrs = self._find_router_subnets_cidrs(ctx_elevated, router_id)
if remove_snat_rules:
# Be safe and concede NAT rules might not exist.
# Therefore use min_num_expected=0
for cidr in cidrs:
nvplib.delete_nat_rules_by_match(
self.cluster, router_id, "SourceNatRule",
max_num_expected=1, min_num_expected=0,
source_ip_addresses=cidr)
if add_snat_rules:
ip_addresses = self._build_ip_address_list(
ctx_elevated, router.gw_port['fixed_ips'])
# Set the SNAT rule for each subnet (only first IP)
for cidr in cidrs:
cidr_prefix = int(cidr.split('/')[1])
nvplib.create_lrouter_snat_rule(
self.cluster, router_id,
ip_addresses[0].split('/')[0],
ip_addresses[0].split('/')[0],
order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
match_criteria={'source_ip_addresses': cidr})
def _update_router_port_attachment(self, cluster, context,
router_id, port_data,
nvp_router_port_id,
@ -526,15 +582,6 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
"L3GatewayAttachment",
ext_network[pnet.PHYSICAL_NETWORK],
ext_network[pnet.SEGMENTATION_ID])
# Set the SNAT rule for each subnet (only first IP)
for cidr in self._find_router_subnets_cidrs(context, router_id):
cidr_prefix = int(cidr.split('/')[1])
nvplib.create_lrouter_snat_rule(
self.cluster, router_id,
ip_addresses[0].split('/')[0],
ip_addresses[0].split('/')[0],
order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
match_criteria={'source_ip_addresses': cidr})
LOG.debug(_("_nvp_create_ext_gw_port completed on external network "
"%(ext_net_id)s, attached to router:%(router_id)s. "
@ -559,13 +606,6 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
port_data['name'],
True,
['0.0.0.0/31'])
# Delete the SNAT rule for each subnet, keep in mind
# that the rule might have already been removed from NVP
for cidr in self._find_router_subnets_cidrs(context, router_id):
nvplib.delete_nat_rules_by_match(
self.cluster, router_id, "SourceNatRule",
max_num_expected=1, min_num_expected=0,
source_ip_addresses=cidr)
# Reset attachment
self._update_router_port_attachment(
self.cluster, context, router_id, port_data,
@ -1654,7 +1694,7 @@ class NvpPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
# Fetch router from DB
router = self._get_router(context, router_id)
gw_port = router.gw_port
if gw_port:
if gw_port and router.enable_snat:
# There is a change gw_port might have multiple IPs
# In that case we will consider only the first one
if gw_port.get('fixed_ips'):

View File

@ -39,6 +39,7 @@ from neutron.plugins.nicira import nvplib
from neutron.tests.unit.nicira import fake_nvpapiclient
import neutron.tests.unit.nicira.test_networkgw as test_l2_gw
import neutron.tests.unit.test_db_plugin as test_plugin
import neutron.tests.unit.test_extension_ext_gw_mode as test_ext_gw_mode
import neutron.tests.unit.test_extension_portsecurity as psec
import neutron.tests.unit.test_extension_security_group as ext_sg
from neutron.tests.unit import test_extensions
@ -830,6 +831,11 @@ class TestNiciraQoSQueue(NiciraPluginV2TestCase):
self.assertEqual(queue['qos_queue']['max'], 20)
class NiciraExtGwModeTestCase(test_ext_gw_mode.ExtGwModeTestCase,
NiciraPluginV2TestCase):
pass
class NiciraNeutronNVPOutOfSync(test_l3_plugin.L3NatTestCaseBase,
NiciraPluginV2TestCase):

View File

@ -302,7 +302,7 @@ class ExtGwModeTestCase(test_db_plugin.NeutronDbPluginV2TestCase,
test_l3_plugin.L3NatTestCaseMixin):
def setUp(self):
# Store l3 resource attribute map as it's will be updated
# Store l3 resource attribute map as it will be updated
self._l3_attribute_map_bk = {}
for item in l3.RESOURCE_ATTRIBUTE_MAP:
self._l3_attribute_map_bk[item] = (