CORD-537 Added flow rules for vSG connectivity

- Added Q_IN_Q table
- Added flow rules for vSG connectivity
- Changed to listen port update event from Neutron to update vSG IPs

Change-Id: I227ba7a91e90ec0752481ebf623b4e848d585265
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtn.java b/src/main/java/org/onosproject/cordvtn/CordVtn.java
index e27aa74..e071550 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -38,7 +38,6 @@
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.Port;
-import org.onosproject.net.SparseAnnotations;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
@@ -137,10 +136,14 @@
             };
 
     private static final String DEFAULT_TUNNEL = "vxlan";
-    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
     private static final String SERVICE_ID = "serviceId";
-    private static final String LOCATION_IP = "locationIp";
     private static final String OPENSTACK_VM_ID = "openstackVmId";
+    private static final String OPENSTACK_PORT_ID = "openstackPortId";
+    private static final String DATA_PLANE_IP = "dataPlaneIp";
+    private static final String DATA_PLANE_INTF = "dataPlaneIntf";
+    private static final String S_TAG = "stag";
+
+    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
 
     private final ExecutorService eventExecutor =
             newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "event-handler"));
@@ -263,18 +266,24 @@
         }
 
         Set<IpAddress> ip = Sets.newHashSet(vPort.fixedIps().values());
-        SparseAnnotations annotations = DefaultAnnotations.builder()
-                .set(OPENSTACK_VM_ID, vPort.deviceId())
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
                 .set(SERVICE_ID, vPort.networkId())
-                .set(LOCATION_IP, node.dpIp().ip().toString())
-                .build();
+                .set(OPENSTACK_VM_ID, vPort.deviceId())
+                .set(OPENSTACK_PORT_ID, vPort.id())
+                .set(DATA_PLANE_IP, node.dpIp().ip().toString())
+                .set(DATA_PLANE_INTF, node.dpIntf());
+
+        String serviceVlan = getServiceVlan(vPort);
+        if (serviceVlan != null) {
+            annotations.set(S_TAG, serviceVlan);
+        }
 
         HostDescription hostDesc = new DefaultHostDescription(
                 mac,
                 VlanId.NONE,
                 new HostLocation(connectPoint, System.currentTimeMillis()),
                 ip,
-                annotations);
+                annotations.build());
 
         hostProvider.hostDetected(hostId, hostDesc, false);
     }
@@ -294,6 +303,20 @@
         hostProvider.hostVanished(host.id());
     }
 
+    @Override
+    public void updateVirtualSubscriberGateways(HostId vSgHostId, String serviceVlan,
+                                                Set<IpAddress> vSgIps) {
+        Host vSgVm = hostService.getHost(vSgHostId);
+
+        if (vSgVm == null || !vSgVm.annotations().value(S_TAG).equals(serviceVlan)) {
+            log.debug("Invalid vSG updates for {}", serviceVlan);
+            return;
+        }
+
+        log.info("Updates vSGs in {} with {}", vSgVm.id(), vSgIps.toString());
+        ruleInstaller.populateSubscriberGatewayRules(vSgVm, vSgIps);
+    }
+
     /**
      * Returns CordService by service ID.
      *
@@ -357,10 +380,11 @@
      * Returns IP address for tunneling for a given host.
      *
      * @param host host
-     * @return ip address
+     * @return ip address, or null
      */
     private IpAddress getTunnelIp(Host host) {
-        return IpAddress.valueOf(host.annotations().value(LOCATION_IP));
+        String ip = host.annotations().value(DATA_PLANE_IP);
+        return ip == null ? null : IpAddress.valueOf(ip);
     }
 
     /**
@@ -374,6 +398,22 @@
     }
 
     /**
+     * Returns s-tag from a given OpenStack port.
+     *
+     * @param vPort openstack port
+     * @return s-tag string
+     */
+    private String getServiceVlan(OpenstackPort vPort) {
+        checkNotNull(vPort);
+
+        if (vPort.name() != null && vPort.name().startsWith(S_TAG)) {
+            return vPort.name().split("-")[1];
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Returns hosts associated with a given OpenStack network.
      *
      * @param vNet openstack network
@@ -395,6 +435,30 @@
     }
 
     /**
+     * Returns public ip addresses of vSGs running inside a give vSG host.
+     *
+     * @param vSgHost vSG host
+     * @return set of ip address, or empty set
+     */
+    private Set<IpAddress> getSubscriberGatewayIps(Host vSgHost) {
+        String vPortId = vSgHost.annotations().value(OPENSTACK_PORT_ID);
+        String serviceVlan = vSgHost.annotations().value(S_TAG);
+
+        OpenstackPort vPort = openstackService.port(vPortId);
+        if (vPort == null) {
+            log.warn("Failed to get OpenStack port {} for VM {}", vPortId, vSgHost.id());
+            return Sets.newHashSet();
+        }
+
+        if (!serviceVlan.equals(getServiceVlan(vPort))) {
+            log.error("Host({}) s-tag does not match with vPort s-tag", vSgHost.id());
+            return Sets.newHashSet();
+        }
+
+        return vPort.allowedAddressPairs().keySet();
+    }
+
+    /**
      * Registers static DHCP lease for a given host.
      *
      * @param host host
@@ -452,8 +516,13 @@
             arpProxy.sendGratuitousArp(service.serviceIp(), gatewayMac, Sets.newHashSet(host));
         }
 
-        ruleInstaller.populateBasicConnectionRules(host, getTunnelIp(host), vNet);
         registerDhcpLease(host, service);
+        ruleInstaller.populateBasicConnectionRules(host, getTunnelIp(host), vNet);
+
+        if (host.annotations().value(S_TAG) != null) {
+            log.debug("vSG VM detected {}", host.id());
+            ruleInstaller.populateSubscriberGatewayRules(host, getSubscriberGatewayIps(host));
+        }
     }
 
     /**
@@ -468,7 +537,7 @@
         }
 
         String vNetId = host.annotations().value(SERVICE_ID);
-        OpenstackNetwork vNet = openstackService.network(host.annotations().value(SERVICE_ID));
+        OpenstackNetwork vNet = openstackService.network(vNetId);
         if (vNet == null) {
             log.warn("Failed to get OpenStack network {} for VM {}({}).",
                      vNetId,