Listen on openstack's port event to create instance port and host

Change-Id: I3ef48dea08bf26bb61ed05d19b47d2bbade181e6
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 897f473..e05c0d4 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -16,11 +16,13 @@
 package org.onosproject.openstacknetworking.api;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Provides constants used in OpenStackSwitching.
@@ -133,6 +135,8 @@
     public static final String GRE = "GRE";
     public static final String GENEVE = "GENEVE";
 
+    public static final Set<String> TUNNEL_TYPE = ImmutableSet.of(VXLAN, GRE, GENEVE);
+
     public static Map<String, String> portNamePrefixMap() {
         return PORT_NAME_PREFIX_MAP;
     }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
index 1b42ccf..04ec1e7 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -42,6 +42,8 @@
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.openstacknetworking.api.Constants;
 import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
@@ -71,6 +73,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_PREFIX_VM;
 import static org.onosproject.openstacknetworking.api.Constants.PORT_NAME_VHOST_USER_PREFIX_VM;
+import static org.onosproject.openstacknetworking.api.Constants.TUNNEL_TYPE;
 import static org.onosproject.openstacknetworking.api.Constants.portNamePrefixMap;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.vnicType;
@@ -112,6 +115,8 @@
             groupedThreads(this.getClass().getSimpleName(), "device-event"));
     private final InternalDeviceListener internalDeviceListener =
             new InternalDeviceListener();
+    private final InternalNetworkListener internalNetworkListener =
+            new InternalNetworkListener();
     private final InternalOpenstackNodeListener internalNodeListener =
             new InternalOpenstackNodeListener();
 
@@ -127,6 +132,7 @@
         coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
         deviceService.addListener(internalDeviceListener);
         osNodeService.addListener(internalNodeListener);
+        osNetworkService.addListener(internalNetworkListener);
         hostProviderService = hostProviderRegistry.register(this);
 
         log.info("Started");
@@ -136,6 +142,7 @@
     protected void deactivate() {
         hostProviderRegistry.unregister(this);
         osNodeService.removeListener(internalNodeListener);
+        osNetworkService.removeListener(internalNetworkListener);
         deviceService.removeListener(internalDeviceListener);
 
         executor.shutdown();
@@ -158,7 +165,11 @@
         log.debug("Instance port {} is detected from {}",
                 event.port().annotations().value(PORT_NAME),
                 event.subject().id());
-        processPortAdded(event.port());
+        // we check the existence of openstack port, in case VM creation
+        // event comes before port creation
+        if (osNetworkService.port(event.port()) != null) {
+            processPortAdded(event.port());
+        }
     }
 
     /**
@@ -171,7 +182,11 @@
         log.debug("Instance port {} is removed from {}",
                 event.port().annotations().value(PORT_NAME),
                 event.subject().id());
-        processPortRemoved(event.port());
+        // we check the existence of openstack port, will not remove any hosts
+        // or instance ports with non-exist openstack port
+        if (osNetworkService.port(event.port()) != null) {
+            processPortRemoved(event.port());
+        }
     }
 
     /**
@@ -441,4 +456,30 @@
                     });
         }
     }
+
+    private class InternalNetworkListener implements OpenstackNetworkListener {
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_PORT_CREATED:
+                    executor.execute(() -> processOpenstackPortAddition(event));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void processOpenstackPortAddition(OpenstackNetworkEvent event) {
+            String portId = event.port().getId();
+            deviceService.getDevices().forEach(device -> {
+                deviceService.getPorts(device.id()).stream()
+                        .filter(Port::isEnabled)
+                        .filter(p -> p.annotations().value(PORT_NAME) != null)
+                        .filter(p -> portId.contains(p.annotations().value(PORT_NAME).substring(3)))
+                        .filter(p -> !TUNNEL_TYPE.contains(p.annotations().value(PORT_NAME).toUpperCase()))
+                        .findAny().ifPresent(OpenstackSwitchingHostProvider.this::processPortAdded);
+            });
+        }
+    }
 }