From 70518f2c19a25697ad385f68ee76ee908501e7cb Mon Sep 17 00:00:00 2001
From: Gary Kotton <gkotton@vmware.com>
Date: Thu, 22 Feb 2018 02:33:46 -0800
Subject: [PATCH] NSX|V3: router attached to VLAN must have gateway

Validate that the router does not remove the gateway if a VLAN is
attached. This is due to the fact that the router is centralized
and must be on a edge cluster.

Change-Id: I0d2d0ddb3c4beb8beeac9942db1e0259a5faf3a7
---
 vmware_nsx/plugins/nsx_v3/plugin.py | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py
index b345fcb7eb..4f9e672716 100644
--- a/vmware_nsx/plugins/nsx_v3/plugin.py
+++ b/vmware_nsx/plugins/nsx_v3/plugin.py
@@ -3467,20 +3467,27 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
         router_data = router['router']
         self._assert_on_router_admin_state(router_data)
 
-        # if setting this router as no-snat, make sure gw address scope match
-        # those of the subnets
-        if (validators.is_attr_set(gw_info) and
-            not gw_info.get('enable_snat', cfg.CONF.enable_snat_by_default)):
+        if validators.is_attr_set(gw_info):
             router_ports = self._get_router_interfaces(context, router_id)
             for port in router_ports:
-                for fip in port['fixed_ips']:
-                    self._validate_address_scope_for_router_interface(
-                        context.elevated(), router_id,
-                        gw_info['network_id'], fip['subnet_id'])
+                # if setting this router as no-snat, make sure gw address scope
+                # match those of the subnets
+                if not gw_info.get('enable_snat',
+                                   cfg.CONF.enable_snat_by_default):
+                    for fip in port['fixed_ips']:
+                        self._validate_address_scope_for_router_interface(
+                            context.elevated(), router_id,
+                            gw_info['network_id'], fip['subnet_id'])
+                # If the network attached to a router is a VLAN backed network
+                # then it must be attached to a edge cluster
+                if (not gw_info and
+                    not self._is_overlay_network(context, port['network_id'])):
+                    msg = _("A router attached to a VLAN backed network "
+                            "must have a external network assigned.")
+                    raise n_exc.InvalidInput(error_message=msg)
 
-        # VPNaaS need to be notified on router GW changes (there is currently
-        # no matching upstream registration for this)
-        if validators.is_attr_set(gw_info):
+            # VPNaaS need to be notified on router GW changes (there is
+            # currentlyno matching upstream registration for this)
             vpn_plugin = directory.get_plugin(plugin_const.VPN)
             if vpn_plugin:
                 vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider]