[ONOS-5911] Port removal after receiving OFPR_DELETE port status reason.

Change-Id: I1f3f6c752da5f989a2d727f378e9f04fbbb71341
diff --git a/core/api/src/main/java/org/onosproject/net/device/DefaultPortDescription.java b/core/api/src/main/java/org/onosproject/net/device/DefaultPortDescription.java
index 89cc6e8..cc28227 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DefaultPortDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DefaultPortDescription.java
@@ -34,6 +34,7 @@
 
     private final PortNumber number;
     private final boolean isEnabled;
+    private final boolean isRemoved;
     private final Type type;
     private final long portSpeed;
 
@@ -61,9 +62,26 @@
     public DefaultPortDescription(PortNumber number, boolean isEnabled,
                                   Type type, long portSpeed,
                                   SparseAnnotations...annotations) {
+        this(number, isEnabled, false, type, portSpeed, annotations);
+    }
+
+    /**
+     * Creates a port description using the supplied information.
+     *
+     * @param number      port number
+     * @param isEnabled   port enabled state
+     * @param isRemoved   port removed state
+     * @param type        port type
+     * @param portSpeed   port speed in Mbps
+     * @param annotations optional key/value annotations map
+     */
+    public DefaultPortDescription(PortNumber number, boolean isEnabled, boolean isRemoved,
+                                  Type type, long portSpeed,
+                                  SparseAnnotations...annotations) {
         super(annotations);
         this.number = checkNotNull(number);
         this.isEnabled = isEnabled;
+        this.isRemoved = isRemoved;
         this.type = type;
         this.portSpeed = portSpeed;
     }
@@ -72,6 +90,7 @@
     protected DefaultPortDescription() {
         this.number = null;
         this.isEnabled = false;
+        this.isRemoved = false;
         this.portSpeed = DEFAULT_SPEED;
         this.type = Type.COPPER;
     }
@@ -112,6 +131,11 @@
     }
 
     @Override
+    public boolean isRemoved() {
+        return isRemoved;
+    }
+
+    @Override
     public Type type() {
         return type;
     }
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java b/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
index f8c9442..ff34bd2 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
@@ -55,6 +55,17 @@
     void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions);
 
     /**
+     * Delete information about a single port of a device.
+     * It is up to the core to determine what has changed.
+     *
+     * @param deviceId         identity of the device
+     * @param portDescription       device port description
+     */
+    default void deletePort(DeviceId deviceId, PortDescription portDescription) {
+
+    }
+
+    /**
      * Notifies the core about port status change of a single port.
      *
      * @param deviceId        identity of the device
diff --git a/core/api/src/main/java/org/onosproject/net/device/PortDescription.java b/core/api/src/main/java/org/onosproject/net/device/PortDescription.java
index 31680e5..020794b 100644
--- a/core/api/src/main/java/org/onosproject/net/device/PortDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/device/PortDescription.java
@@ -40,6 +40,13 @@
     boolean isEnabled();
 
     /**
+     * Indicates whether or not the port was removed.
+     *
+     * @return true if the port is removed.
+     */
+    boolean isRemoved();
+
+    /**
      * Returns the port type.
      *
      * @return port type
diff --git a/core/api/src/test/java/org/onosproject/net/device/DeviceProviderServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/device/DeviceProviderServiceAdapter.java
index d6660d8..b471298 100644
--- a/core/api/src/test/java/org/onosproject/net/device/DeviceProviderServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/device/DeviceProviderServiceAdapter.java
@@ -42,6 +42,11 @@
     }
 
     @Override
+    public void deletePort(DeviceId deviceId, PortDescription portDescription) {
+
+    }
+
+    @Override
     public void portStatusChanged(DeviceId deviceId, PortDescription portDescription) {
 
     }
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 6fbb21c..3b50882 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
@@ -592,6 +592,41 @@
         }
 
         @Override
+        public void deletePort(DeviceId deviceId, PortDescription basePortDescription) {
+
+            checkNotNull(deviceId, DEVICE_ID_NULL);
+            checkNotNull(basePortDescription, PORT_DESCRIPTION_NULL);
+            checkValidity();
+
+            if (!mastershipService.isLocalMaster(deviceId)) {
+                // Never been a master for this device
+                // any update will be ignored.
+                log.trace("Ignoring {} port update on standby node. {}", deviceId,
+                          basePortDescription);
+                return;
+            }
+
+            Device device = getDevice(deviceId);
+            if (device == null) {
+                log.trace("Device not found: {}", deviceId);
+            }
+
+            PortDescription newPortDescription = new DefaultPortDescription(basePortDescription.portNumber(),
+                                                                            basePortDescription.isEnabled(),
+                                                                            true,
+                                                                            basePortDescription.type(),
+                                                                            basePortDescription.portSpeed(),
+                                                                            basePortDescription.annotations());
+            final DeviceEvent event = store.updatePortStatus(this.provider().id(),
+                                                             deviceId,
+                                                             newPortDescription);
+            if (event != null) {
+                log.info("Device {} port {} status changed", deviceId, event.port().number());
+                post(event);
+            }
+        }
+
+        @Override
         public void receivedRoleReply(DeviceId deviceId, MastershipRole requested,
                                       MastershipRole response) {
             // Several things can happen here:
diff --git a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
index 4e6c05d..95db702 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
@@ -736,6 +736,7 @@
     private DeviceEvent updatePort(Device device, Port oldPort,
                                    Port newPort,
                                    Map<PortNumber, Port> ports) {
+
         if (oldPort.isEnabled() != newPort.isEnabled() ||
                 oldPort.type() != newPort.type() ||
                 oldPort.portSpeed() != newPort.portSpeed() ||
@@ -746,6 +747,14 @@
         return null;
     }
 
+    private DeviceEvent removePort(DeviceId deviceId, PortNumber portNumber) {
+
+        log.info("Deleted port: " + deviceId.toString() + "/" + portNumber.toString());
+        Port deletedPort = devicePorts.get(deviceId).remove(portNumber);
+
+        return new DeviceEvent(PORT_REMOVED, getDevice(deviceId), deletedPort);
+    }
+
     // Prunes the specified list of ports based on which ports are in the
     // processed list and returns list of corresponding events.
     // Guarded by deviceDescs value (=Device lock)
@@ -857,14 +866,16 @@
             final PortNumber number = deltaDesc.value().portNumber();
             final Port oldPort = ports.get(number);
             final Port newPort;
-
             final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
+            boolean toDelete = false;
+
             if (existingPortDesc == null ||
                     deltaDesc.isNewer(existingPortDesc)) {
                 // on new port or valid update
                 // update description
                 descs.putPortDesc(deltaDesc);
                 newPort = composePort(device, number, descsMap);
+                toDelete = deltaDesc.value().isRemoved();
             } else {
                 // same or outdated event, ignored.
                 log.trace("ignore same or outdated {} >= {}", existingPortDesc, deltaDesc);
@@ -874,7 +885,7 @@
             if (oldPort == null) {
                 return createPort(device, newPort, ports);
             } else {
-                return updatePort(device, oldPort, newPort, ports);
+                return toDelete ? removePort(deviceId, number) : updatePort(device, oldPort, newPort, ports);
             }
         }
     }
diff --git a/incubator/rpc-grpc/src/main/java/org/onosproject/incubator/rpc/grpc/DeviceProviderServiceClientProxy.java b/incubator/rpc-grpc/src/main/java/org/onosproject/incubator/rpc/grpc/DeviceProviderServiceClientProxy.java
index c93a42a..bd133ad 100644
--- a/incubator/rpc-grpc/src/main/java/org/onosproject/incubator/rpc/grpc/DeviceProviderServiceClientProxy.java
+++ b/incubator/rpc-grpc/src/main/java/org/onosproject/incubator/rpc/grpc/DeviceProviderServiceClientProxy.java
@@ -129,6 +129,11 @@
     }
 
     @Override
+    public void deletePort(DeviceId deviceId, PortDescription portDescription) {
+
+    }
+
+    @Override
     public void portStatusChanged(DeviceId deviceId,
                                   PortDescription portDescription) {
         checkValidity();
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index 6119a1f..0acd186 100644
--- a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -450,7 +450,11 @@
         public void portChanged(Dpid dpid, OFPortStatus status) {
             LOG.debug("portChanged({},{})", dpid, status);
             PortDescription portDescription = buildPortDescription(status);
-            providerService.portStatusChanged(deviceId(uri(dpid)), portDescription);
+            if (status.getReason() != OFPortReason.DELETE) {
+                providerService.portStatusChanged(deviceId(uri(dpid)), portDescription);
+            } else {
+                providerService.deletePort(deviceId(uri(dpid)), portDescription);
+            }
         }
 
         @Override
@@ -819,7 +823,7 @@
                 PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
                 Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
                 SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString());
-                return new DefaultPortDescription(portNo, false, type,
+                return new DefaultPortDescription(portNo, false, true, type,
                                                   portSpeed(port), annotations);
             }
         }