[ONOS-5682] Introduced VIRTUAL_PORT_* events for virtual network to virtual port mapping changes.

Change-Id: Ic1d39643b35601aa12c7e703a73cf9258bd04aa2
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
index 7fde1e3..7234b70 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
@@ -57,10 +57,23 @@
         /**
          * Signifies that a virtual network device was removed.
          */
-        VIRTUAL_DEVICE_REMOVED
+        VIRTUAL_DEVICE_REMOVED,
+        /**
+         * Signifies that a new virtual network port was added.
+         */
+        VIRTUAL_PORT_ADDED,
+        /**
+         * Signifies that a virtual network port was updated.
+         */
+        VIRTUAL_PORT_UPDATED,
+        /**
+         * Signifies that a virtual network port was removed.
+         */
+        VIRTUAL_PORT_REMOVED
     }
 
     private final VirtualDevice virtualDevice;
+    private final VirtualPort virtualPort;
 
     /**
      * Creates an event of a given type and for the specified subject.
@@ -69,7 +82,7 @@
      * @param subject     event subject
      */
     public VirtualNetworkEvent(Type type, NetworkId subject) {
-        this(type, subject, null);
+        this(type, subject, null, null);
     }
 
     /**
@@ -81,41 +94,108 @@
      * @param virtualDevice virtual device
      */
     public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice) {
+        this(type, subject, virtualDevice, null);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject and the
+     * virtual port.
+     *
+     * @param type          event type
+     * @param subject       event subject
+     * @param virtualPort   virtual port
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualPort virtualPort) {
+        this(type, subject, null, virtualPort);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject, virtual device and
+     * virtual port.
+     *
+     * @param type          event type
+     * @param subject       event subject
+     * @param virtualDevice virtual device
+     * @param virtualPort   virtual port
+     */
+    private VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+                                VirtualPort virtualPort) {
         super(type, subject);
         this.virtualDevice = virtualDevice;
+        this.virtualPort = virtualPort;
     }
 
     /**
      * Creates an event of a given type and for the specified subject and time.
      *
-     * @param type        device event type
+     * @param type        event type
      * @param subject     event subject
      * @param time        occurrence time
      */
     public VirtualNetworkEvent(Type type, NetworkId subject, long time) {
-        this(type, subject, null, time);
+        this(type, subject, null, null, time);
     }
 
     /**
      * Creates an event of a given type and for the specified subject, virtual device and time.
      *
-     * @param type          device event type
+     * @param type          event type
      * @param subject       event subject
      * @param virtualDevice virtual device
      * @param time          occurrence time
      */
-    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice, long time) {
+    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+                               long time) {
+        this(type, subject, virtualDevice, null, time);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject, virtual port and time.
+     *
+     * @param type          device event type
+     * @param subject       event subject
+     * @param virtualPort   virtual port
+     * @param time          occurrence time
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualPort virtualPort,
+                               long time) {
+        this(type, subject, null, virtualPort, time);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject, virtual device,
+     * virtual port and time.
+     *
+     * @param type          device event type
+     * @param subject       event subject
+     * @param virtualDevice virtual device
+     * @param virtualPort   virtual port
+     * @param time          occurrence time
+     */
+    private VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+                                VirtualPort virtualPort, long time) {
         super(type, subject, time);
         this.virtualDevice = virtualDevice;
+        this.virtualPort = virtualPort;
     }
 
     /**
      * Returns virtual device affected by event - may be null (for events relating to
-     * tenants and virtual networks).
+     * tenants, virtual networks and virtual ports).
      *
      * @return virtual device
      */
     public VirtualDevice virtualDevice() {
         return virtualDevice;
     }
+
+    /**
+     * Returns virtual port affected by event - may be null (for events relating to
+     * tenants, virtual networks and virtual devices).
+     *
+     * @return virtual port
+     */
+    public VirtualPort virtualPort() {
+        return virtualPort;
+    }
 }
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
index 3a580eb..b9f259b 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -400,21 +400,28 @@
     }
 
     /**
-     * Tests add and remove of virtual ports.
+     * Tests add, bind and remove of virtual ports.
      */
     @Test
     public void testAddRemoveVirtualPort() {
+        List<VirtualNetworkEvent.Type> expectedEventTypes = new ArrayList<>();
+
         manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        expectedEventTypes.add(VirtualNetworkEvent.Type.TENANT_REGISTERED);
         VirtualNetwork virtualNetwork1 =
                 manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+        expectedEventTypes.add(VirtualNetworkEvent.Type.NETWORK_ADDED);
         VirtualDevice virtualDevice =
                 manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
         ConnectPoint cp = new ConnectPoint(virtualDevice.id(), PortNumber.portNumber(1));
 
         manager.createVirtualPort(virtualNetwork1.id(),
                                   virtualDevice.id(), PortNumber.portNumber(1), cp);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
         manager.createVirtualPort(virtualNetwork1.id(),
                                   virtualDevice.id(), PortNumber.portNumber(2), cp);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
 
         Set<VirtualPort> virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), virtualDevice.id());
         assertNotNull("The virtual port set should not be null", virtualPorts);
@@ -423,6 +430,7 @@
         for (VirtualPort virtualPort : virtualPorts) {
             manager.removeVirtualPort(virtualNetwork1.id(),
                                       (DeviceId) virtualPort.element().id(), virtualPort.number());
+            expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED);
             // attempt to remove the same virtual port again.
             manager.removeVirtualPort(virtualNetwork1.id(),
                                       (DeviceId) virtualPort.element().id(), virtualPort.number());
@@ -434,10 +442,22 @@
         VirtualPort virtualPort =
                 manager.createVirtualPort(virtualNetwork1.id(), virtualDevice.id(),
                                                             PortNumber.portNumber(1), cp);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
+
+        ConnectPoint newCp = new ConnectPoint(DID2, PortNumber.portNumber(2));
+        manager.bindVirtualPort(virtualNetwork1.id(), virtualDevice.id(),
+                                PortNumber.portNumber(1), newCp);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED);
+
         manager.removeVirtualPort(virtualNetwork1.id(),
                                   (DeviceId) virtualPort.element().id(), virtualPort.number());
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED);
         virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), virtualDevice.id());
         assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+
+        // Validate that the events were all received in the correct order.
+        validateEvents((Enum[]) expectedEventTypes.toArray(
+                new VirtualNetworkEvent.Type[expectedEventTypes.size()]));
     }
 
     /**
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 967504c..17d8dcb 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -74,6 +74,7 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -558,6 +559,8 @@
                                                          portNumber, realizedBy);
         virtualPortSet.add(virtualPort);
         networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+        notifyDelegate(new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED,
+                                               networkId, virtualPort));
         return virtualPort;
     }
 
@@ -581,6 +584,8 @@
         vPort = new DefaultVirtualPort(networkId, device, portNumber, realizedBy);
         virtualPortSet.add(vPort);
         networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+        notifyDelegate(new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED,
+                                               networkId, vPort));
     }
 
     @Override
@@ -594,14 +599,22 @@
             }
         });
 
-        if (virtualPortSet != null) {
+        if (!virtualPortSet.isEmpty()) {
+            AtomicBoolean portRemoved = new AtomicBoolean(false);
             networkIdVirtualPortSetMap.compute(networkId, (id, existingVirtualPorts) -> {
                 if (existingVirtualPorts == null || existingVirtualPorts.isEmpty()) {
                     return new HashSet<>();
                 } else {
+                    portRemoved.set(true);
                     return new HashSet<>(Sets.difference(existingVirtualPorts, virtualPortSet));
                 }
             });
+            if (portRemoved.get()) {
+                virtualPortSet.forEach(virtualPort -> notifyDelegate(
+                        new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED,
+                                                networkId, virtualPort)
+                ));
+            }
         }
     }