[ONOS-4205] support east-west routing in openstackRouting

  - Supports east-west routing traffic
  - Fixes javadocs

Change-Id: I23d9b9497cc2be667fbc9554812c7f5b49c35364
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
index 62e8cdf..781dfa7 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
@@ -37,6 +37,8 @@
 import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
@@ -115,12 +117,15 @@
     private ApplicationId appId;
     private ConsistentMap<Integer, String> tpPortNumMap; // Map<PortNum, allocated VM`s Mac & destionation Ip address>
     private ConsistentMap<String, OpenstackFloatingIP> floatingIpMap; // Map<FloatingIp`s Id, FloatingIp object>
+    // Map<RouterInterface`s portId, Corresponded port`s network id>
+    private ConsistentMap<String, String> routerInterfaceMap;
     private static final String APP_ID = "org.onosproject.openstackrouting";
     private static final String PORT_NAME = "portName";
     private static final String PORTNAME_PREFIX_VM = "tap";
     private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
     private static final String FLOATING_IP_MAP_NAME = "openstackrouting-floatingip";
-    private static final String TP_PORT_MAP_NAME = "openstackrouting-portnum";
+    private static final String TP_PORT_MAP_NAME = "openstackrouting-tpportnum";
+    private static final String ROUTER_INTERFACE_MAP_NAME = "openstackrouting-routerinterface";
     private static final String COLON = ":";
     private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
     private static final int TP_PORT_MINIMUM_NUM = 1024;
@@ -152,7 +157,13 @@
             .register(Integer.class)
             .register(String.class);
 
+    private static final KryoNamespace.Builder ROUTER_INTERFACE_SERIALIZER = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(KryoNamespaces.MISC)
+            .register(String.class);
+
     private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
+    private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
     private ExecutorService l3EventExecutorService =
             Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
     private ExecutorService icmpEventExecutorService =
@@ -170,6 +181,7 @@
         packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
         configRegistry.registerConfigFactory(configFactory);
         configService.addListener(configListener);
+        deviceService.addListener(internalDeviceListener);
 
         floatingIpMap = storageService.<String, OpenstackFloatingIP>consistentMapBuilder()
                 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
@@ -181,52 +193,59 @@
                 .withName(TP_PORT_MAP_NAME)
                 .withApplicationId(appId)
                 .build();
+        routerInterfaceMap = storageService.<String, String>consistentMapBuilder()
+                .withSerializer(Serializer.using(ROUTER_INTERFACE_SERIALIZER.build()))
+                .withName(ROUTER_INTERFACE_MAP_NAME)
+                .withApplicationId(appId)
+                .build();
 
         readConfiguration();
 
-        log.info("onos-openstackrouting started");
+        log.info("started");
     }
 
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(internalPacketProcessor);
+        deviceService.removeListener(internalDeviceListener);
         l3EventExecutorService.shutdown();
         icmpEventExecutorService.shutdown();
         arpEventExecutorService.shutdown();
 
         floatingIpMap.clear();
         tpPortNumMap.clear();
+        routerInterfaceMap.clear();
 
-        log.info("onos-openstackrouting stopped");
+        log.info("stopped");
     }
 
 
     @Override
-    public void createFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
-        floatingIpMap.put(openstackFloatingIP.id(), openstackFloatingIP);
+    public void createFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
+        floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
     }
 
     @Override
-    public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
-        if (!floatingIpMap.containsKey(openstackFloatingIP.id())) {
-            log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIP.id());
+    public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
+        if (!floatingIpMap.containsKey(openstackFloatingIp.id())) {
+            log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIp.id());
             return;
         }
-        if (openstackFloatingIP.portId() == null || openstackFloatingIP.portId().equals("null")) {
-            OpenstackFloatingIP floatingIP = floatingIpMap.get(openstackFloatingIP.id()).value();
+        if (openstackFloatingIp.portId() == null || openstackFloatingIp.portId().equals("null")) {
+            OpenstackFloatingIP floatingIp = floatingIpMap.get(openstackFloatingIp.id()).value();
             OpenstackPortInfo portInfo = openstackSwitchingService.openstackPortInfo()
-                    .get(PORTNAME_PREFIX_VM.concat(floatingIP.portId().substring(0, 11)));
+                    .get(PORTNAME_PREFIX_VM.concat(floatingIp.portId().substring(0, 11)));
             if (portInfo == null) {
-                log.warn("There`s no portInfo information about portId {}", floatingIP.portId());
+                log.warn("There`s no portInfo information about portId {}", floatingIp.portId());
                 return;
             }
             l3EventExecutorService.execute(
-                    new OpenstackFloatingIPHandler(rulePopulator, floatingIP, false, portInfo));
-            floatingIpMap.replace(floatingIP.id(), openstackFloatingIP);
+                    new OpenstackFloatingIPHandler(rulePopulator, floatingIp, false, portInfo));
+            floatingIpMap.replace(floatingIp.id(), openstackFloatingIp);
         } else {
-            floatingIpMap.put(openstackFloatingIP.id(), openstackFloatingIP);
+            floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
             l3EventExecutorService.execute(
-                    new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIP, true, null));
+                    new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIp, true, null));
         }
     }
 
@@ -280,11 +299,69 @@
         List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
         routerInterfaces.add(routerInterface);
         checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
+        setL3Connection(getOpenstackRouter(routerInterface.id()), null);
+        routerInterfaceMap.put(routerInterface.portId(), openstackService.port(routerInterface.portId()).networkId());
+    }
+
+    private void setL3Connection(OpenstackRouter openstackRouter, OpenstackPort openstackPort) {
+        Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(openstackRouter);
+        if (interfaceList.size() < 2) {
+            return;
+        }
+        if (openstackPort == null) {
+            interfaceList.forEach(i -> {
+                OpenstackPort interfacePort = openstackService.port(i.portId());
+                openstackService.ports()
+                        .stream()
+                        .filter(p -> p.networkId().equals(interfacePort.networkId())
+                                && !p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
+                        .forEach(p -> rulePopulator.populateL3Rules(p,
+                                getL3ConnectionList(p.networkId(), interfaceList)));
+            });
+        } else {
+            rulePopulator.populateL3Rules(openstackPort, getL3ConnectionList(openstackPort.networkId(), interfaceList));
+        }
+
+    }
+
+    private List<OpenstackRouterInterface> getL3ConnectionList(String networkId,
+                                                               Collection<OpenstackRouterInterface> interfaceList) {
+        List<OpenstackRouterInterface> targetList = Lists.newArrayList();
+        interfaceList.forEach(i -> {
+            OpenstackPort port = openstackService.port(i.portId());
+            if (!port.networkId().equals(networkId)) {
+                targetList.add(i);
+            }
+        });
+        return targetList;
     }
 
     @Override
     public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
+        OpenstackRouter router = openstackService.router(routerInterface.id());
+        Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(router);
+        if (interfaceList.size() == 1) {
+            List<OpenstackRouterInterface> newList = Lists.newArrayList();
+            newList.add(routerInterface);
+            interfaceList.forEach(i -> removeL3RulesForRouterInterface(i, router, newList));
+        }
+        removeL3RulesForRouterInterface(routerInterface, router, null);
         rulePopulator.removeExternalRules(routerInterface);
+        routerInterfaceMap.remove(routerInterface.portId());
+    }
+
+    private void removeL3RulesForRouterInterface(OpenstackRouterInterface routerInterface, OpenstackRouter router,
+                                                 List<OpenstackRouterInterface> newList) {
+        openstackService.ports(routerInterfaceMap.get(routerInterface.portId()).value()).forEach(p -> {
+                    Ip4Address vmIp = (Ip4Address) p.fixedIps().values().toArray()[0];
+                    if (newList == null) {
+                        rulePopulator.removeL3Rules(vmIp,
+                                getL3ConnectionList(p.networkId(), getOpenstackRouterInterface(router)));
+                    } else {
+                        rulePopulator.removeL3Rules(vmIp, newList);
+                    }
+                }
+        );
     }
 
     @Override
@@ -313,6 +390,11 @@
         }
     }
 
+    @Override
+    public String networkIdForRouterInterface(String portId) {
+        return routerInterfaceMap.get(portId).value();
+    }
+
     private Collection<OpenstackFloatingIP> associatedFloatingIps() {
         List<OpenstackFloatingIP> fIps = Lists.newArrayList();
         floatingIpMap.values()
@@ -327,10 +409,7 @@
                         openstackService.ports()
                                 .stream()
                                 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
-                                .forEach(p -> {
-                                    OpenstackRouterInterface routerInterface = portToRouterInterface(p);
-                                    updateRouterInterface(routerInterface);
-                                })
+                                .forEach(p -> updateRouterInterface(portToRouterInterface(p)))
         );
 
     }
@@ -382,14 +461,13 @@
                         int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
                         Optional<Port> port =
                                 getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
-
-                        if (!port.isPresent()) {
-                            log.warn("There`s no external interface");
-                        } else {
+                        if (port.isPresent()) {
                             OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(),
                                     Ip4Address.valueOf(iPacket.getSourceAddress()));
                             l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context,
                                     portNum, openstackPort, port.get(), config));
+                        } else {
+                            log.warn("There`s no external interface");
                         }
                         break;
                 }
@@ -471,22 +549,20 @@
         List<OpenstackRouterInterface> interfaces = Lists.newArrayList();
         openstackService.ports()
                 .stream()
-                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
-                .filter(p -> p.deviceId().equals(router.id()))
-                .forEach(p -> {
-                    interfaces.add(portToRouterInterface(p));
-                });
+                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
+                        && p.deviceId().equals(router.id()))
+                .forEach(p -> interfaces.add(portToRouterInterface(p)));
         return interfaces;
     }
 
     private OpenstackRouter getOpenstackRouter(String id) {
         return openstackService.routers().stream().filter(r ->
-                r.id().equals(id)).findAny().orElse(null);
+                r.id().equals(id)).iterator().next();
     }
 
     private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) {
         OpenstackPort openstackPort = openstackService.ports().stream()
-                .filter(p -> p.macAddress().equals(sourceMac)).findFirst().orElse(null);
+                .filter(p -> p.macAddress().equals(sourceMac)).iterator().next();
         return openstackPort.fixedIps().values().stream().filter(ip ->
                 ip.equals(ip4Address)).count() > 0 ? openstackPort : null;
     }
@@ -534,4 +610,38 @@
             }
         }
     }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent deviceEvent) {
+            if (deviceEvent.type() == DeviceEvent.Type.PORT_UPDATED) {
+                Port port = deviceEvent.port();
+                OpenstackPortInfo portInfo = openstackSwitchingService.openstackPortInfo()
+                        .get(port.annotations().value(PORT_NAME));
+                OpenstackPort openstackPort = openstackService.port(port);
+                OpenstackPort interfacePort = openstackService.ports()
+                        .stream()
+                        .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
+                                && p.networkId().equals(openstackPort.networkId()))
+                        .findAny()
+                        .orElse(null);
+                if (portInfo == null && openstackPort == null) {
+                    log.warn("As delete event timing issue between routing and switching, Can`t delete L3 rules");
+                    return;
+                }
+                if ((port.isEnabled()) && (interfacePort != null)) {
+                    OpenstackRouterInterface routerInterface = portToRouterInterface(interfacePort);
+                    l3EventExecutorService.execute(() ->
+                            setL3Connection(getOpenstackRouter(routerInterface.id()), openstackPort));
+                } else if (interfacePort != null) {
+                    OpenstackRouterInterface routerInterface = portToRouterInterface(interfacePort);
+                    l3EventExecutorService.execute(() -> rulePopulator.removeL3Rules(portInfo.ip(),
+                            getL3ConnectionList(portInfo.networkId(),
+                                    getOpenstackRouterInterface(getOpenstackRouter(routerInterface.id())))));
+                }
+            }
+        }
+    }
+
 }