ONOS-3404 Change p2p tunnel to p2any tunnel by using set tunnel_dst in
flow.

Change-Id: I06cb6d42772434de9c016e795de5c6d8a6f45dfb
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VTNManager.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VTNManager.java
index 7192ad9..2e38251 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VTNManager.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VTNManager.java
@@ -15,12 +15,14 @@
  */
 package org.onosproject.vtn.manager.impl;
 
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -30,6 +32,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.util.KryoNamespace;
@@ -43,6 +46,7 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.BridgeConfig;
 import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
 import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.config.basics.BasicDeviceConfig;
 import org.onosproject.net.device.DeviceEvent;
@@ -50,15 +54,24 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment.Builder;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.LogicalClockService;
-import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.vtn.manager.VTNService;
 import org.onosproject.vtn.table.ClassifierService;
@@ -79,6 +92,7 @@
 import org.onosproject.vtnrsc.virtualport.VirtualPortService;
 import org.slf4j.Logger;
 
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 /**
@@ -120,6 +134,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
     private ApplicationId appId;
     private ClassifierService classifierService;
     private L2ForwardService l2ForwardService;
@@ -133,9 +150,10 @@
     private static final String EX_PORT_NAME = "eth0";
     private static final String SWITCHES_OF_CONTROLLER = "switchesOfController";
     private static final String SWITCH_OF_LOCAL_HOST_PORTS = "switchOfLocalHostPorts";
+    private static final String DEFAULT_IP = "0.0.0.0";
 
     private EventuallyConsistentMap<IpAddress, Boolean> switchesOfController;
-    private ConsistentMap<DeviceId, NetworkOfLocalHostPorts> switchOfLocalHostPorts;
+    private EventuallyConsistentMap<DeviceId, NetworkOfLocalHostPorts> switchOfLocalHostPorts;
 
     @Activate
     public void activate() {
@@ -162,9 +180,9 @@
                 .build();
 
         switchOfLocalHostPorts = storageService
-                .<DeviceId, NetworkOfLocalHostPorts>consistentMapBuilder()
-                .withName(SWITCH_OF_LOCAL_HOST_PORTS)
-                .withSerializer(Serializer.using(serializer.build()))
+                .<DeviceId, NetworkOfLocalHostPorts>eventuallyConsistentMapBuilder()
+                .withName(SWITCH_OF_LOCAL_HOST_PORTS).withSerializer(serializer)
+                .withTimestampProvider((k, v) -> clockService.getTimestamp())
                 .build();
 
         log.info("Started");
@@ -251,44 +269,10 @@
 
     private void programTunnelConfig(DeviceId localDeviceId, IpAddress localIp,
                                      DriverHandler localHandler) {
-        Iterable<Device> devices = deviceService.getAvailableDevices();
-        Sets.newHashSet(devices).stream()
-                .filter(d -> Device.Type.CONTROLLER == d.type())
-                .filter(d -> !localDeviceId.equals(d.id())).forEach(d -> {
-                    DriverHandler tunHandler = driverService
-                            .createHandler(d.id());
-                    String remoteIpAddress = d.annotations()
-                            .value(CONTROLLER_IP_KEY);
-                    IpAddress remoteIp = IpAddress.valueOf(remoteIpAddress);
-                    if (remoteIp.toString()
-                            .equalsIgnoreCase(localIp.toString())) {
-                        log.error("The localIp and remoteIp are the same");
-                        return;
-                    }
-                    if (localHandler != null) {
-                        // Create tunnel in br-int on local controller
-                        if (mastershipService.isLocalMaster(localDeviceId)) {
-                            VtnConfig.applyTunnelConfig(localHandler, localIp, remoteIp);
-                            log.info("Add tunnel between {} and {}", localIp,
-                                     remoteIp);
-                        }
-                        // Create tunnel in br-int on other controllers
-                        if (mastershipService.isLocalMaster(d.id())) {
-                            VtnConfig.applyTunnelConfig(tunHandler, remoteIp,
-                                                        localIp);
-                            log.info("Add tunnel between {} and {}", remoteIp,
-                                     localIp);
-                        }
-                    } else {
-                        // remove tunnel in br-int on other controllers
-                        if (mastershipService.isLocalMaster(d.id())) {
-                            VtnConfig.removeTunnelConfig(tunHandler, remoteIp,
-                                                        localIp);
-                            log.info("Remove tunnel between {} and {}", remoteIp,
-                                     localIp);
-                        }
-                    }
-                });
+        if (mastershipService.isLocalMaster(localDeviceId)) {
+            VtnConfig.applyTunnelConfig(localHandler, localIp, IpAddress.valueOf(DEFAULT_IP));
+            log.info("Add tunnel on {}", localIp);
+        }
     }
 
     private void applyTunnelOut(Device device, Objective.Operation type) {
@@ -321,6 +305,7 @@
         DriverHandler handler = driverService.createHandler(localControllerId);
         Set<PortNumber> ports = VtnConfig.getPortNumbers(handler);
         Iterable<Host> allHosts = hostService.getHosts();
+        String tunnelName = "vxlan-" + DEFAULT_IP;
         if (allHosts != null) {
             Sets.newHashSet(allHosts).stream().forEach(host -> {
                 MacAddress hostMac = host.mac();
@@ -346,13 +331,12 @@
                 }
                 IpAddress remoteIpAddress = IpAddress
                         .valueOf(remoteControllerIp);
-                String tunnelName = "vxlan-" + remoteIpAddress.toString();
                 ports.stream()
                         .filter(p -> p.name().equalsIgnoreCase(tunnelName))
                         .forEach(p -> {
                     l2ForwardService
                             .programTunnelOut(device.id(), segmentationId, p,
-                                              hostMac, type);
+                                              hostMac, type, remoteIpAddress);
                 });
             });
         }
@@ -392,16 +376,11 @@
         Collection<PortNumber> localTunnelPorts = VtnData.getLocalTunnelPorts(ports);
         // Get all the local vm's PortNumber in the current node
         Map<TenantNetworkId, Set<PortNumber>> localHostPorts = switchOfLocalHostPorts
-                .get(deviceId).value().getNetworkOfLocalHostPorts();
+                .get(deviceId).getNetworkOfLocalHostPorts();
         Set<PortNumber> networkOflocalHostPorts = localHostPorts.get(network.id());
-
-        l2ForwardService.programLocalBcastRules(deviceId, segmentationId,
-                                                inPort, networkOflocalHostPorts,
-                                                localTunnelPorts,
-                                                type);
-
-        l2ForwardService.programLocalOut(deviceId, segmentationId, inPort, mac,
-                                         type);
+        for (PortNumber p : localTunnelPorts) {
+            programGroupTable(deviceId, appId, p, devices, type);
+        }
 
         if (type == Objective.Operation.ADD) {
             if (networkOflocalHostPorts == null) {
@@ -409,20 +388,33 @@
                 localHostPorts.putIfAbsent(network.id(), networkOflocalHostPorts);
             }
             networkOflocalHostPorts.add(inPort);
+            l2ForwardService.programLocalBcastRules(deviceId, segmentationId,
+                                                    inPort, networkOflocalHostPorts,
+                                                    localTunnelPorts,
+                                                    type);
             classifierService.programTunnelIn(deviceId, segmentationId,
                                               localTunnelPorts,
                                               type);
         } else if (type == Objective.Operation.REMOVE) {
-            networkOflocalHostPorts.remove(inPort);
-            if (networkOflocalHostPorts.isEmpty()) {
-                classifierService.programTunnelIn(deviceId, segmentationId,
-                                                  localTunnelPorts,
-                                                  Objective.Operation.REMOVE);
-                switchOfLocalHostPorts.get(deviceId).value().getNetworkOfLocalHostPorts()
-                                            .remove(virtualPort.networkId());
+            if (networkOflocalHostPorts != null) {
+                l2ForwardService.programLocalBcastRules(deviceId, segmentationId,
+                                                        inPort, networkOflocalHostPorts,
+                                                        localTunnelPorts,
+                                                        type);
+                networkOflocalHostPorts.remove(inPort);
+                if (networkOflocalHostPorts.isEmpty()) {
+                    classifierService.programTunnelIn(deviceId, segmentationId,
+                                                      localTunnelPorts,
+                                                      type);
+                    switchOfLocalHostPorts.get(deviceId).getNetworkOfLocalHostPorts()
+                                                .remove(virtualPort.networkId());
+                }
             }
         }
 
+        l2ForwardService.programLocalOut(deviceId, segmentationId, inPort, mac,
+                                         type);
+
         l2ForwardService.programTunnelBcastRules(deviceId, segmentationId,
                                                  networkOflocalHostPorts,
                                                  localTunnelPorts,
@@ -440,9 +432,11 @@
                                    SegmentationId segmentationId,
                                    MacAddress dstMac,
                                    Objective.Operation type) {
-        String tunnelName = "vxlan-" + ipAddress.toString();
+        String tunnelName = "vxlan-" + DEFAULT_IP;
         Sets.newHashSet(devices).stream()
-                .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> {
+                .filter(d -> d.type() == Device.Type.CONTROLLER)
+                .filter(d -> !("ovsdb:" + ipAddress).equals(d.id().toString()))
+                .forEach(d -> {
                     DriverHandler handler = driverService.createHandler(d.id());
                     BridgeConfig bridgeConfig = handler
                             .behaviour(BridgeConfig.class);
@@ -459,7 +453,7 @@
                                 .forEach(p -> {
                             l2ForwardService.programTunnelOut(sw.deviceId(),
                                                               segmentationId, p,
-                                                              dstMac, type);
+                                                              dstMac, type, ipAddress);
                         });
                     }
                 });
@@ -525,4 +519,45 @@
         }
     }
 
+    private void programGroupTable(DeviceId deviceId, ApplicationId appid,
+                                   PortNumber portNumber, Iterable<Device> devices, Objective.Operation type) {
+        if (type.equals(Objective.Operation.REMOVE)) {
+            return;
+        }
+
+        List<GroupBucket> buckets = Lists.newArrayList();
+        Sets.newHashSet(devices)
+        .stream()
+        .filter(d -> d.type() == Device.Type.CONTROLLER)
+        .filter(d -> !deviceId.equals(d.id()))
+        .forEach(d -> {
+                    String ipAddress = d.annotations()
+                             .value(CONTROLLER_IP_KEY);
+                    Ip4Address dst = Ip4Address.valueOf(ipAddress);
+                    Builder builder = DefaultTrafficTreatment.builder();
+
+                    DriverHandler handler = driverService.createHandler(deviceId);
+                    ExtensionTreatmentResolver resolver =  handler.behaviour(ExtensionTreatmentResolver.class);
+                    ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+                    try {
+                        treatment.setPropertyValue("tunnelDst", dst);
+                    } catch (Exception e) {
+                       log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
+                    }
+
+                    builder.extension(treatment, deviceId);
+                    builder.setOutput(portNumber);
+                    GroupBucket bucket = DefaultGroupBucket
+                            .createAllGroupBucket(builder.build());
+                    buckets.add(bucket);
+                 });
+        final GroupKey key = new DefaultGroupKey(APP_ID.getBytes());
+        GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
+                                                                        GroupDescription.Type.ALL,
+                                                                        new GroupBuckets(buckets),
+                                                                        key,
+                                                                        L2ForwardServiceImpl.GROUP_ID,
+                                                                        appid);
+        groupService.addGroup(groupDescription);
+    }
 }