Port Annotation bug fix

1. Handled the case when InternalNetworkConfigListener in DeviceManager recieves an event associated with PortAnotationConfig class.
2. Added CONFIG_REMOVED event type in InternalNetworkConfigListener in DeviceManager.
3. Changed comine function in PortAnnotationOperator to take care of removing old annotations from PortDescription which are not in current
PortAnnotationConfig.

Tested using 'annotate-ports' command and 'ports' command

Change-Id: Ie4d2b529c2f559a40a296d916193318e0ccc7b93
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
index a967c70..036ac0c 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
@@ -1063,13 +1063,20 @@
     private class InternalNetworkConfigListener implements NetworkConfigListener {
         @Override
         public boolean isRelevant(NetworkConfigEvent event) {
+            DeviceId deviceId;
+            if (event.configClass().equals(PortAnnotationConfig.class)) {
+                deviceId = ((ConnectPoint) event.subject()).deviceId();
+            } else {
+                deviceId = (DeviceId) event.subject();
+            }
             return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
-                    || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)
+                    || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED
+                    || event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED)
                     && (event.configClass().equals(BasicDeviceConfig.class)
                     || portOpsIndex.containsKey(event.configClass())
                     || event.configClass().equals(PortDescriptionsConfig.class)
                     || event.configClass().equals(DeviceAnnotationConfig.class))
-                    && mastershipService.isLocalMaster((DeviceId) event.subject());
+                    && mastershipService.isLocalMaster(deviceId);
         }
 
         @Override
@@ -1127,7 +1134,7 @@
                 //       but cannot add new port purely from Config.
                 de = Optional.ofNullable(dp)
                         .map(provider -> store.getPortDescription(provider.id(), did, cpt.port()))
-                        .map(desc -> applyAllPortOps(cpt, desc))
+                        .map(desc -> applyAllPortOps(cpt, desc, event.prevConfig()))
                         .map(desc -> store.updatePortStatus(dp.id(), did, desc))
                         .orElse(null);
             }
@@ -1228,6 +1235,23 @@
     }
 
     /**
+     * Merges the appropriate PortConfig with the description.
+     *
+     * @param cpt  ConnectPoint where the port is attached
+     * @param desc {@link PortDescription}
+     * @param prevConfig previous configuration
+     * @return merged {@link PortDescription}
+     */
+    private PortDescription applyAllPortOps(ConnectPoint cpt, PortDescription desc,
+                                            Optional<Config> prevConfig) {
+        PortDescription work = desc;
+        for (PortConfigOperator portOp : portOps) {
+            work = portOp.combine(cpt, work, prevConfig);
+        }
+        return portAnnotationOp.combine(cpt, work, prevConfig);
+    }
+
+    /**
      * Port Enable/Disable message sent to the device's MASTER node.
      */
     private class InternalPortUpDownEvent {
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/PortAnnotationOperator.java b/core/net/src/main/java/org/onosproject/net/device/impl/PortAnnotationOperator.java
index ac9ea84..700325f 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/PortAnnotationOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/PortAnnotationOperator.java
@@ -15,11 +15,14 @@
  */
 package org.onosproject.net.device.impl;
 
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultAnnotations.Builder;
+import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.config.PortConfigOperator;
 import org.onosproject.net.config.basics.PortAnnotationConfig;
@@ -72,8 +75,33 @@
         builder.putAll(annotations);
 
         return DefaultPortDescription.builder(descr)
-                    .annotations(builder.build())
-                    .build();
+                .annotations(builder.build())
+                .build();
+    }
+
+    @Override
+    public PortDescription combine(ConnectPoint cp, PortDescription descr,
+                                   Optional<Config> prevConfig) {
+        PortAnnotationConfig cfg = lookupConfig(cp);
+        Map<String, String> annotations = new HashMap<>();
+        if (cfg != null) {
+            annotations.putAll(cfg.annotations());
+        }
+
+        Builder builder = DefaultAnnotations.builder();
+        builder.putAll(descr.annotations());
+        if (prevConfig.isPresent()) {
+            PortAnnotationConfig prevDeviceAnnotationConfig = (PortAnnotationConfig) prevConfig.get();
+            for (String key : prevDeviceAnnotationConfig.annotations().keySet()) {
+                if (!annotations.containsKey(key)) {
+                    builder.remove(key);
+                }
+            }
+        }
+        builder.putAll(annotations);
+        return DefaultPortDescription.builder(descr)
+                .annotations(builder.build())
+                .build();
     }
 
 }