Skip to content

Commit

Permalink
Nicira NVP plugin support for l3_ext_gw_mode extension
Browse files Browse the repository at this point in the history
Bug 1121129

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

Change-Id: I531ebe0053b1b9e21d6f0685776acebe3173b170
  • Loading branch information
salv-orlando committed Jul 15, 2013
1 parent 69ebd08 commit 60a392f
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 22 deletions.
9 changes: 7 additions & 2 deletions neutron/db/l3_gwmode_db.py
Expand Up @@ -43,8 +43,10 @@ def _make_router_dict(self, router, fields=None):
'enable_snat': router.enable_snat}
return self._fields(res, fields)

def _update_router_gw_info(self, context, router_id, info):
router = self._get_router(context, router_id)
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)
enable_snat = not info or info.get('enable_snat', router.enable_snat)
Expand All @@ -54,6 +56,9 @@ def _update_router_gw_info(self, context, router_id, info):
# 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 = {}
Expand Down
Expand Up @@ -34,6 +34,7 @@
'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'
]
Expand Down
78 changes: 59 additions & 19 deletions neutron/plugins/nicira/NeutronPlugin.py
Expand Up @@ -44,6 +44,7 @@
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
Expand Down Expand Up @@ -73,6 +74,7 @@


LOG = logging.getLogger("NeutronPlugin")

NVP_NOSNAT_RULES_ORDER = 10
NVP_FLOATINGIP_NAT_RULES_ORDER = 224
NVP_EXTGW_NAT_RULES_ORDER = 255
Expand Down Expand Up @@ -127,6 +129,7 @@ def create_rpc_dispatcher(self):

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,
Expand All @@ -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",
Expand Down Expand Up @@ -278,6 +282,58 @@ def _create_and_attach_router_port(self, cluster, context,
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,
Expand Down Expand Up @@ -526,15 +582,6 @@ def _nvp_create_ext_gw_port(self, context, port_data):
"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. "
Expand All @@ -559,13 +606,6 @@ def _nvp_delete_ext_gw_port(self, context, port_data):
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,
Expand Down Expand Up @@ -1654,7 +1694,7 @@ def add_router_interface(self, context, router_id, interface_info):
# 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'):
Expand Down Expand Up @@ -1869,7 +1909,7 @@ def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
match_criteria={'destination_ip_addresses':
floating_ip})
# setup snat rule such that src ip of a IP packet when
# using floating is the floating ip itself.
# using floating is the floating ip itself.
nvplib.create_lrouter_snat_rule(
self.cluster, router_id, floating_ip, floating_ip,
order=NVP_FLOATINGIP_NAT_RULES_ORDER,
Expand Down
6 changes: 6 additions & 0 deletions neutron/tests/unit/nicira/test_nicira_plugin.py
Expand Up @@ -39,6 +39,7 @@
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
Expand Down Expand Up @@ -830,6 +831,11 @@ def test_rxtx_factor(self):
self.assertEqual(queue['qos_queue']['max'], 20)


class NiciraExtGwModeTestCase(test_ext_gw_mode.ExtGwModeTestCase,
NiciraPluginV2TestCase):
pass


class NiciraNeutronNVPOutOfSync(test_l3_plugin.L3NatTestCaseBase,
NiciraPluginV2TestCase):

Expand Down
2 changes: 1 addition & 1 deletion neutron/tests/unit/test_extension_ext_gw_mode.py
Expand Up @@ -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] = (
Expand Down

0 comments on commit 60a392f

Please sign in to comment.