SONA: OpenstackSwitching - remove flows

 - Remove the corresponding flows when VMs are removed.
 - Remove the IP mapping of the VM removed from the DHCP service (even when doNotPushFlows is true)
 - Updated the network config json file to reflect the change of cordvtn
Change-Id: I4c359d456422ece37b93e6366f2fd4daaf081a37
diff --git a/apps/openstackswitching/api/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingService.java b/apps/openstackswitching/api/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingService.java
index 59b8db0..be566f0 100644
--- a/apps/openstackswitching/api/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingService.java
+++ b/apps/openstackswitching/api/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingService.java
@@ -35,13 +35,14 @@
      * Removes flow rules corresponding to the port removed by Openstack.
      *
      */
-    void deletePorts();
+    void deletePort(String uuid);
 
     /**
      * Updates flow rules corresponding to the port information updated by Openstack.
      *
+     * @param openstackPort
      */
-    void updatePorts();
+    void updatePort(OpenstackPort openstackPort);
 
     /**
      * Stores the network information created by openstack.
diff --git a/apps/openstackswitching/app/pom.xml b/apps/openstackswitching/app/pom.xml
index 7d26f8f..719b049 100644
--- a/apps/openstackswitching/app/pom.xml
+++ b/apps/openstackswitching/app/pom.xml
@@ -40,6 +40,9 @@
         </api.description>
         <api.package>org.onosproject.openstackswitching.web</api.package>
         <onos.app.origin>SKT, Inc.</onos.app.origin>
+        <onos.app.requires>
+            org.onosproject.dhcp
+        </onos.app.requires>
     </properties>
 
 
diff --git a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingManager.java b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingManager.java
index 5a19ed0..b5e0df3 100644
--- a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingManager.java
+++ b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingManager.java
@@ -25,11 +25,14 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcp.DhcpService;
+import org.onosproject.event.AbstractEvent;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
 import org.onosproject.net.Port;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -39,7 +42,16 @@
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
@@ -48,6 +60,7 @@
 import org.slf4j.LoggerFactory;
 import java.util.List;
 import java.util.Collection;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -76,6 +89,9 @@
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowObjectiveService flowObjectiveService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -87,6 +103,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DriverService driverService;
 
+    protected FlowRuleService flowRuleService;
+
     private ApplicationId appId;
     private boolean doNotPushFlows;
     private Ip4Address neutronServer;
@@ -101,6 +119,7 @@
     private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
     private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
     private InternalConfigListener internalConfigListener = new InternalConfigListener();
+    private InternalHostListener internalHostListener = new InternalHostListener();
     private final Set<ConfigFactory> factories = ImmutableSet.of(
             new ConfigFactory<ApplicationId, OpenstackSwitchingConfig>(APP_SUBJECT_FACTORY,
                     OpenstackSwitchingConfig.class,
@@ -120,6 +139,7 @@
         factories.forEach(cfgService::registerConfigFactory);
         packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
         deviceService.addListener(internalDeviceListener);
+        hostService.addListener(internalHostListener);
         cfgService.addListener(internalConfigListener);
 
         internalConfigListener.configureNetwork();
@@ -132,6 +152,7 @@
         packetService.removeProcessor(internalPacketProcessor);
         deviceService.removeListener(internalDeviceListener);
         cfgService.removeListener(internalConfigListener);
+        factories.forEach(cfgService::unregisterConfigFactory);
 
         deviceEventExcutorService.shutdown();
 
@@ -144,13 +165,12 @@
     }
 
     @Override
-    public void deletePorts() {
+    public void deletePort(String uuid) {
 
     }
 
     @Override
-    public void updatePorts() {
-
+    public void updatePort(OpenstackPort openstackPort) {
     }
 
     @Override
@@ -201,7 +221,8 @@
     }
 
     private void processPortAdded(Device device, Port port) {
-        if (!port.annotations().value("portName").equals("vxlan")) {
+        if (!port.annotations().value("portName").equals("vxlan")
+            && port.isEnabled() && !doNotPushFlows) {
             OpenstackSwitchingRulePopulator rulePopulator =
                     new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
                             deviceService, restHandler, driverService);
@@ -210,7 +231,6 @@
     }
 
     private void processPortRemoved(Device device, Port port) {
-        // TODO: Remove flow rules for the VM removed
         log.debug("port {} is removed", port.toString());
     }
 
@@ -238,6 +258,50 @@
         );
     }
 
+    private void processHostRemoved(Host host) {
+        log.debug("host {} was removed", host.toString());
+
+        try {
+            if (!doNotPushFlows) {
+                IpAddress hostIp = host.ipAddresses().stream().
+                        filter(ip -> ip.isIp4()).findAny().orElse(null);
+                OpenstackSwitchingRulePopulator rulePopulator =
+                        new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
+                                deviceService, restHandler, driverService);
+                rulePopulator.removeSwitchingRules(host.location().deviceId(),
+                        hostIp.getIp4Address());
+            }
+
+            dhcpService.removeStaticMapping(host.mac());
+        } catch (NoSuchElementException e) {
+            log.error("No IP address is assigned.");
+        }
+    }
+
+    private long getVniFromFlowRules(DeviceId deviceId, Ip4Address hostIp) {
+
+        for (FlowEntry flowEntry: flowRuleService.getFlowEntries(deviceId)) {
+            Criterion c = flowEntry.selector().getCriterion(Criterion.Type.IPV4_DST);
+            if (c != null) {
+                IPCriterion destIpCriterion = (IPCriterion) c;
+                if (destIpCriterion.ip().getIp4Prefix().address().equals(hostIp)) {
+                    for (Instruction i : flowEntry.treatment().immediate()) {
+                        if (i.type().equals(Instruction.Type.L2MODIFICATION)) {
+                            L2ModificationInstruction l2m = (L2ModificationInstruction) i;
+                            if (l2m.subtype().equals(L2ModificationInstruction.L2SubType.TUNNEL_ID)) {
+                                L2ModificationInstruction.ModTunnelIdInstruction setTunnelInstr =
+                                        (L2ModificationInstruction.ModTunnelIdInstruction) l2m;
+                                return setTunnelInstr.tunnelId();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return 0;
+    }
+
     private void registerDhcpInfo(OpenstackPort openstackPort) {
         Ip4Address ip4Address;
         Ip4Address subnetMask;
@@ -301,6 +365,14 @@
         }
     }
 
+    private class InternalHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent hostEvent) {
+            deviceEventExcutorService.execute(new InternalEventHandler(hostEvent));
+        }
+    }
+
     private class InternalDeviceListener implements DeviceListener {
 
         @Override
@@ -311,46 +383,56 @@
 
     private class InternalEventHandler implements Runnable {
 
-        volatile DeviceEvent deviceEvent;
+        volatile AbstractEvent event;
 
-        InternalEventHandler(DeviceEvent deviceEvent) {
-            this.deviceEvent = deviceEvent;
+        InternalEventHandler(AbstractEvent event) {
+            this.event = event;
         }
 
         @Override
         public void run() {
 
-            if (doNotPushFlows) {
-                return;
-            }
+            if (event instanceof  DeviceEvent) {
+                DeviceEvent deviceEvent = (DeviceEvent) event;
 
-            switch (deviceEvent.type()) {
-                case DEVICE_ADDED:
-                    processDeviceAdded((Device) deviceEvent.subject());
-                    break;
-                case DEVICE_UPDATED:
-                    Port port = (Port) deviceEvent.subject();
-                    if (port.isEnabled()) {
+                switch (deviceEvent.type()) {
+                    case DEVICE_ADDED:
+                        processDeviceAdded((Device) deviceEvent.subject());
+                        break;
+                    case DEVICE_UPDATED:
+                        Port port = (Port) deviceEvent.subject();
+                        if (port.isEnabled()) {
+                            processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
+                        }
+                        break;
+                    case DEVICE_AVAILABILITY_CHANGED:
+                        Device device = (Device) deviceEvent.subject();
+                        if (deviceService.isAvailable(device.id())) {
+                            processDeviceAdded(device);
+                        }
+                        break;
+                    case PORT_ADDED:
                         processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
-                    }
-                    break;
-                case DEVICE_AVAILABILITY_CHANGED:
-                    Device device = (Device) deviceEvent.subject();
-                    if (deviceService.isAvailable(device.id())) {
-                        processDeviceAdded(device);
-                    }
-                    break;
-                case PORT_ADDED:
-                    processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
-                    break;
-                case PORT_UPDATED:
-                    processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
-                    break;
-                case PORT_REMOVED:
-                    processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
-                    break;
-                default:
-                    break;
+                        break;
+                    case PORT_UPDATED:
+                        processPortAdded((Device) deviceEvent.subject(), deviceEvent.port());
+                        break;
+                    case PORT_REMOVED:
+                        processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
+                        break;
+                    default:
+                        break;
+                }
+            } else if (event instanceof HostEvent) {
+                HostEvent hostEvent = (HostEvent) event;
+
+                switch (hostEvent.type()) {
+                    case HOST_REMOVED:
+                        processHostRemoved((Host) hostEvent.subject());
+                        break;
+                    default:
+                        break;
+                }
             }
         }
     }
@@ -395,5 +477,4 @@
             this.hostIp = hostIp;
         }
     }
-
 }
\ No newline at end of file
diff --git a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingRulePopulator.java b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingRulePopulator.java
index ed89479..8d5c780 100644
--- a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingRulePopulator.java
+++ b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/OpenstackSwitchingRulePopulator.java
@@ -131,6 +131,21 @@
     }
 
     /**
+     * Remove flows rules for the VM removed.
+     *
+     * @param deviceId device to remove rules
+     * @param vmIp IP address of the VM removed
+     */
+    public void removeSwitchingRules(DeviceId deviceId, Ip4Address vmIp) {
+        removeFlowRuleForVMsInSameCnode(deviceId, vmIp);
+        deviceService.getAvailableDevices().forEach(device -> {
+            if (!device.id().equals(deviceId)) {
+                removeVxLanFlowRule(device.id(), vmIp);
+            }
+        });
+    }
+
+    /**
      * Populates the flow rules for traffic to VMs in the same Cnode as the sender.
      *
      * @param device device to put the rules
@@ -170,9 +185,10 @@
                         Ip4Address hostIpx = Ip4Address.valueOf(cidx.split(":")[0]);
                         MacAddress vmMacx = getVmMacAddressForPort(pName);
                         Ip4Address fixedIpx = getFixedIpAddressForPort(pName);
-
-                        setVxLanFlowRule(vni, device.id(), hostIpx, fixedIpx, vmMacx);
-                        setVxLanFlowRule(vni, d.id(), hostIpAddress, fixedIp, vmMac);
+                        if (port.isEnabled()) {
+                            setVxLanFlowRule(vni, device.id(), hostIpx, fixedIpx, vmMacx);
+                            setVxLanFlowRule(vni, d.id(), hostIpAddress, fixedIp, vmMac);
+                        }
                     }
                 });
             }
@@ -246,7 +262,7 @@
                 .findFirst().orElse(null);
 
         if (port == null) {
-            log.error("There is port information for port name {}", portName);
+            log.error("There is no port information for port name {}", portName);
             return null;
         }
 
@@ -341,6 +357,40 @@
         flowObjectiveService.forward(id, fo);
     }
 
+    private void removeFlowRuleForVMsInSameCnode(DeviceId id, Ip4Address vmIp) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(vmIp.toIpPrefix());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(id, fo);
+    }
+
+    private void removeVxLanFlowRule(DeviceId id, Ip4Address vmIp) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        // XXX: Later, more matches will be added when multiple table is implemented.
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(vmIp.toIpPrefix());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(id, fo);
+    }
+
     private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
         Driver driver = driverService.getDriver(id);
         DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
diff --git a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/web/OpenstackPortWebResource.java b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/web/OpenstackPortWebResource.java
index faffa73..261128d 100644
--- a/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/web/OpenstackPortWebResource.java
+++ b/apps/openstackswitching/app/src/main/java/org/onosproject/openstackswitching/web/OpenstackPortWebResource.java
@@ -28,6 +28,7 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -68,12 +69,9 @@
         }
     }
 
+    @Path("{portUUID}")
     @DELETE
-    @Path("{id}")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response deletesPorts(InputStream input) {
-        log.debug("REST API ports is called with {}", input.toString());
+    public Response deletePorts(@PathParam("portUUID") String id) {
         return Response.status(Response.Status.OK).build();
     }
 
@@ -82,7 +80,6 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response updatePorts(InputStream input) {
-        log.info("REST API ports is called with {}", input.toString());
         return Response.status(Response.Status.OK).build();
     }
 }
diff --git a/apps/openstackswitching/network-cfg.json b/apps/openstackswitching/network-cfg.json
index abcd2d1..c185d55 100644
--- a/apps/openstackswitching/network-cfg.json
+++ b/apps/openstackswitching/network-cfg.json
@@ -32,19 +32,31 @@
                 "nodes" : [
                     {
                         "hostname" : "compute-01",
+<<<<<<< HEAD
                         "ovsdbIp" : "10.40.101.208",
+=======
+                        "ovsdbIp" : "127.0.0.1",
+>>>>>>> 6a78e2e... SONA: OpenstackSwitching - remove flows
                         "ovsdbPort" : "6640",
                         "bridgeId" : "of:0000000000000001"
                      },
                      {
                         "hostname" : "compute-02",
+<<<<<<< HEAD
                         "ovsdbIp" : "10.40.101.227",
+=======
+                        "ovsdbIp" : "127.0.0.1",
+>>>>>>> 6a78e2e... SONA: OpenstackSwitching - remove flows
                         "ovsdbPort" : "6640",
                         "bridgeId" : "of:0000000000000002"
                      },
                      {
                          "hostname" : "network",
+<<<<<<< HEAD
                          "ovsdbIp" : "10.40.101.209",
+=======
+                         "ovsdbIp" : "127.0.0.1",
+>>>>>>> 6a78e2e... SONA: OpenstackSwitching - remove flows
                          "ovsdbPort" : "6640",
                          "bridgeId" : "of:0000000000000003"
                      }