ONOS-5682 Introduced VIRTUAL_DEVICE_* events.

Change-Id: I42354c2168775b59c6ca515ac114f0ca31e3bf98
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 6c2727d..7fde1e3 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
@@ -45,18 +45,44 @@
         /**
          * Signifies that a virtual network was removed.
          */
-        NETWORK_REMOVED
+        NETWORK_REMOVED,
+        /**
+         * Signifies that a new virtual network device was added.
+         */
+        VIRTUAL_DEVICE_ADDED,
+        /**
+         * Signifies that a virtual network device was updated.
+         */
+        VIRTUAL_DEVICE_UPDATED,
+        /**
+         * Signifies that a virtual network device was removed.
+         */
+        VIRTUAL_DEVICE_REMOVED
     }
 
+    private final VirtualDevice virtualDevice;
+
     /**
-     * Creates an event of a given type and for the specified subject and the
-     * current time.
+     * Creates an event of a given type and for the specified subject.
      *
      * @param type        event type
      * @param subject     event subject
      */
     public VirtualNetworkEvent(Type type, NetworkId subject) {
+        this(type, subject, null);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject and the
+     * virtual device.
+     *
+     * @param type          event type
+     * @param subject       event subject
+     * @param virtualDevice virtual device
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice) {
         super(type, subject);
+        this.virtualDevice = virtualDevice;
     }
 
     /**
@@ -67,6 +93,29 @@
      * @param time        occurrence time
      */
     public VirtualNetworkEvent(Type type, NetworkId subject, long time) {
+        this(type, subject, null, time);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject, virtual device and time.
+     *
+     * @param type          device event type
+     * @param subject       event subject
+     * @param virtualDevice virtual device
+     * @param time          occurrence time
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice, long time) {
         super(type, subject, time);
+        this.virtualDevice = virtualDevice;
+    }
+
+    /**
+     * Returns virtual device affected by event - may be null (for events relating to
+     * tenants and virtual networks).
+     *
+     * @return virtual device
+     */
+    public VirtualDevice virtualDevice() {
+        return virtualDevice;
     }
 }
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 dafd1f7..98bad26 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
@@ -226,13 +226,20 @@
      */
     @Test
     public void testAddRemoveVirtualDevice() {
+        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);
         VirtualNetwork virtualNetwork2 =
                 manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+        expectedEventTypes.add(VirtualNetworkEvent.Type.NETWORK_ADDED);
         manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
         manager.createVirtualDevice(virtualNetwork2.id(), DID2);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
 
         Set<VirtualDevice> virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
         assertNotNull("The virtual device set should not be null", virtualDevices1);
@@ -244,7 +251,8 @@
 
         for (VirtualDevice virtualDevice : virtualDevices1) {
             manager.removeVirtualDevice(virtualNetwork1.id(), virtualDevice.id());
-            // attempt to remove the same virtual device again.
+            expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED);
+            // attempt to remove the same virtual device again - no event expected.
             manager.removeVirtualDevice(virtualNetwork1.id(), virtualDevice.id());
         }
         virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
@@ -252,14 +260,15 @@
 
         // Add/remove the virtual device again.
         VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
         manager.removeVirtualDevice(virtualDevice.networkId(), virtualDevice.id());
+        expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED);
         virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
         assertTrue("The virtual device set should be empty.", virtualDevices1.isEmpty());
 
         // Validate that the events were all received in the correct order.
-        validateEvents(VirtualNetworkEvent.Type.TENANT_REGISTERED,
-                       VirtualNetworkEvent.Type.NETWORK_ADDED,
-                       VirtualNetworkEvent.Type.NETWORK_ADDED);
+        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 9007c21..967504c 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.function.BiFunction;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -109,7 +110,14 @@
     private Map<NetworkId, VirtualNetwork> networkIdVirtualNetworkMap;
 
     // Listener for virtual network events
-    private final MapEventListener<NetworkId, VirtualNetwork> virtualMapListener = new InternalMapListener();
+    private final MapEventListener<NetworkId, VirtualNetwork> virtualNetworkMapListener =
+            new InternalMapListener<>((mapEventType, virtualNetwork) -> {
+                VirtualNetworkEvent.Type eventType =
+                    mapEventType.equals(MapEvent.Type.INSERT) ? VirtualNetworkEvent.Type.NETWORK_ADDED :
+                    mapEventType.equals(MapEvent.Type.UPDATE) ? VirtualNetworkEvent.Type.NETWORK_UPDATED :
+                    mapEventType.equals(MapEvent.Type.REMOVE) ? VirtualNetworkEvent.Type.NETWORK_REMOVED : null;
+                return eventType == null ? null : new VirtualNetworkEvent(eventType, virtualNetwork.id());
+            });
 
     // Track virtual network IDs by tenant Id
     private ConsistentMap<TenantId, Set<NetworkId>> tenantIdNetworkIdSetConsistentMap;
@@ -119,6 +127,17 @@
     private ConsistentMap<DeviceId, VirtualDevice> deviceIdVirtualDeviceConsistentMap;
     private Map<DeviceId, VirtualDevice> deviceIdVirtualDeviceMap;
 
+    // Listener for virtual device events
+    private final MapEventListener<DeviceId, VirtualDevice> virtualDeviceMapListener =
+            new InternalMapListener<>((mapEventType, virtualDevice) -> {
+                VirtualNetworkEvent.Type eventType =
+                    mapEventType.equals(MapEvent.Type.INSERT) ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED :
+                    mapEventType.equals(MapEvent.Type.UPDATE) ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_UPDATED :
+                    mapEventType.equals(MapEvent.Type.REMOVE) ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED : null;
+                return eventType == null ? null :
+                        new VirtualNetworkEvent(eventType, virtualDevice.networkId(), virtualDevice);
+            });
+
     // Track device IDs by network Id
     private ConsistentMap<NetworkId, Set<DeviceId>> networkIdDeviceIdSetConsistentMap;
     private Map<NetworkId, Set<DeviceId>> networkIdDeviceIdSetMap;
@@ -189,7 +208,7 @@
                 .withName("onos-networkId-virtualnetwork")
                 .withRelaxedReadConsistency()
                 .build();
-        networkIdVirtualNetworkConsistentMap.addListener(virtualMapListener);
+        networkIdVirtualNetworkConsistentMap.addListener(virtualNetworkMapListener);
         networkIdVirtualNetworkMap = networkIdVirtualNetworkConsistentMap.asJavaMap();
 
         tenantIdNetworkIdSetConsistentMap = storageService.<TenantId, Set<NetworkId>>consistentMapBuilder()
@@ -204,6 +223,7 @@
                 .withName("onos-deviceId-virtualdevice")
                 .withRelaxedReadConsistency()
                 .build();
+        deviceIdVirtualDeviceConsistentMap.addListener(virtualDeviceMapListener);
         deviceIdVirtualDeviceMap = deviceIdVirtualDeviceConsistentMap.asJavaMap();
 
         networkIdDeviceIdSetConsistentMap = storageService.<NetworkId, Set<DeviceId>>consistentMapBuilder()
@@ -264,7 +284,8 @@
     @Deactivate
     public void deactivate() {
         tenantIdSet.removeListener(setListener);
-        networkIdVirtualNetworkConsistentMap.removeListener(virtualMapListener);
+        networkIdVirtualNetworkConsistentMap.removeListener(virtualNetworkMapListener);
+        deviceIdVirtualDeviceConsistentMap.removeListener(virtualDeviceMapListener);
         log.info("Stopped");
     }
 
@@ -772,29 +793,40 @@
     /**
      * Listener class to map listener map events to the virtual network events.
      */
-    private class InternalMapListener implements MapEventListener<NetworkId, VirtualNetwork> {
+    private class InternalMapListener<K, V> implements MapEventListener<K, V> {
+
+        private final BiFunction<MapEvent.Type, V, VirtualNetworkEvent> createEvent;
+
+        InternalMapListener(BiFunction<MapEvent.Type, V, VirtualNetworkEvent> createEvent) {
+            this.createEvent = createEvent;
+        }
+
         @Override
-        public void event(MapEvent<NetworkId, VirtualNetwork> event) {
-            NetworkId networkId = checkNotNull(event.key());
-            VirtualNetworkEvent.Type type = null;
+        public void event(MapEvent<K, V> event) {
+            checkNotNull(event.key());
+            VirtualNetworkEvent vnetEvent = null;
             switch (event.type()) {
                 case INSERT:
-                    type = VirtualNetworkEvent.Type.NETWORK_ADDED;
+                    vnetEvent = createEvent.apply(event.type(), event.newValue().value());
                     break;
                 case UPDATE:
                     if ((event.oldValue().value() != null) && (event.newValue().value() == null)) {
-                        type = VirtualNetworkEvent.Type.NETWORK_REMOVED;
+                        vnetEvent = createEvent.apply(MapEvent.Type.REMOVE, event.oldValue().value());
                     } else {
-                        type = VirtualNetworkEvent.Type.NETWORK_UPDATED;
+                        vnetEvent = createEvent.apply(event.type(), event.newValue().value());
                     }
                     break;
                 case REMOVE:
-                    type = VirtualNetworkEvent.Type.NETWORK_REMOVED;
+                    if (event.oldValue() != null) {
+                        vnetEvent = createEvent.apply(event.type(), event.oldValue().value());
+                    }
                     break;
                 default:
                     log.error("Unsupported event type: " + event.type());
             }
-            notifyDelegate(new VirtualNetworkEvent(type, networkId));
+            if (vnetEvent != null) {
+                notifyDelegate(vnetEvent);
+            }
         }
     }
 }