ONOS-6626 OFAgent - return port info in PortDescStatsReply and PortStatus messages

Change-Id: I79201784f533dae34d8274fe2fb54e259e8a4a48
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchOperationService.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchOperationService.java
index 6e2f5ad..4def7a7 100644
--- a/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchOperationService.java
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchOperationService.java
@@ -28,13 +28,21 @@
 
     /**
      * Processes a new port of the switch.
-     * It sends out FEATURE_REPLY message to the controllers.
+     * It sends out PORT_STATUS asynchronous message to the controllers.
      *
      * @param port virtual port
      */
     void processPortAdded(Port port);
 
     /**
+     * Processes the removal of a port from the switch.
+     * It sends out PORT_STATUS asynchronous message to the controllers.
+     *
+     * @param port virtual port
+     */
+    void processPortRemoved(Port port);
+
+    /**
      * Processes port link down.
      * It sends out PORT_STATUS asynchronous message to the controllers.
      *
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchService.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchService.java
index 898aede..b836e72 100644
--- a/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchService.java
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/api/OFSwitchService.java
@@ -16,6 +16,8 @@
 package org.onosproject.ofagent.api;
 
 import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
 
 import java.util.Set;
 
@@ -38,4 +40,13 @@
      * @return set of openflow switches; empty set if no devices exist on the network
      */
     Set<OFSwitch> ofSwitches(NetworkId networkId);
+
+    /**
+     * Returns all ports of the specified device in the specified network.
+     *
+     * @param networkId network id
+     * @param deviceId device id
+     * @return set of ports; empty set if no ports exist for the specified device
+     */
+    Set<Port> ports(NetworkId networkId, DeviceId deviceId);
 }
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/DefaultOFSwitch.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/DefaultOFSwitch.java
index 8aa6b49..d52654f 100644
--- a/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/DefaultOFSwitch.java
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/DefaultOFSwitch.java
@@ -17,11 +17,15 @@
 
 import com.google.common.collect.ImmutableSet;
 import io.netty.channel.Channel;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.ofagent.api.OFSwitch;
 import org.onosproject.ofagent.api.OFSwitchCapabilities;
+import org.onosproject.ofagent.api.OFSwitchService;
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFEchoReply;
@@ -33,6 +37,9 @@
 import org.projectfloodlight.openflow.protocol.OFHello;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortReason;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFRoleReply;
 import org.projectfloodlight.openflow.protocol.OFRoleRequest;
 import org.projectfloodlight.openflow.protocol.OFSetConfig;
@@ -41,10 +48,13 @@
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -64,8 +74,12 @@
 
     private final Logger log;
 
+    private final OFSwitchService ofSwitchService;
+
     private final DatapathId dpId;
     private final OFSwitchCapabilities capabilities;
+    private final NetworkId networkId;
+    private final DeviceId deviceId;
 
     // miss_send_len field (in OFSetConfig and OFGetConfig messages) indicates the max
     // bytes of a packet that the switch sends to the controller
@@ -77,16 +91,24 @@
 
     private int handshakeTransactionIds = -1;
 
-    private DefaultOFSwitch(DatapathId dpid, OFSwitchCapabilities capabilities) {
+    private DefaultOFSwitch(DatapathId dpid, OFSwitchCapabilities capabilities,
+                            NetworkId networkId, DeviceId deviceId,
+                            OFSwitchService ofSwitchService) {
         this.dpId = dpid;
         this.capabilities = capabilities;
+        this.networkId = networkId;
+        this.deviceId = deviceId;
+        this.ofSwitchService = ofSwitchService;
         log = LoggerFactory.getLogger(getClass().getName() + " : " + dpid);
     }
 
-    public static DefaultOFSwitch of(DatapathId dpid, OFSwitchCapabilities capabilities) {
+    public static DefaultOFSwitch of(DatapathId dpid, OFSwitchCapabilities capabilities,
+                                     NetworkId networkId, DeviceId deviceId,
+                                     ServiceDirectory serviceDirectory) {
         checkNotNull(dpid, "DPID cannot be null");
         checkNotNull(capabilities, "OF capabilities cannot be null");
-        return new DefaultOFSwitch(dpid, capabilities);
+        return new DefaultOFSwitch(dpid, capabilities, networkId, deviceId,
+                                   serviceDirectory.get(OFSwitchService.class));
     }
 
     @Override
@@ -142,8 +164,12 @@
 
     @Override
     public void processPortAdded(Port port) {
-        // TODO generate FEATURES_REPLY message and send it to the controller
-        log.debug("Functionality not yet supported for {}", port);
+        sendPortStatus(port, OFPortReason.ADD);
+    }
+
+    @Override
+    public void processPortRemoved(Port port) {
+        sendPortStatus(port, OFPortReason.DELETE);
     }
 
     @Override
@@ -176,6 +202,32 @@
         log.debug("Functionality not yet supported for {}", msg);
     }
 
+    private void sendPortStatus(Port port, OFPortReason ofPortReason) {
+        Set<Channel> channels = controllerChannels();
+        if (channels.isEmpty()) {
+            log.trace("No channels present.  Port status will not be sent.");
+            return;
+        }
+        OFPortDesc ofPortDesc = portDesc(port);
+        OFPortStatus ofPortStatus = FACTORY.buildPortStatus()
+                .setDesc(ofPortDesc)
+                .setReason(ofPortReason)
+                .build();
+        log.trace("Sending port status {}", ofPortStatus);
+        channels.forEach(channel -> {
+            channel.writeAndFlush(Collections.singletonList(ofPortStatus));
+        });
+    }
+
+    private OFPortDesc portDesc(Port port) {
+        OFPort ofPort = OFPort.of((int) port.number().toLong());
+        // TODO handle port state and other port attributes
+        OFPortDesc ofPortDesc = FACTORY.buildPortDesc()
+                .setPortNo(ofPort)
+                .build();
+        return ofPortDesc;
+    }
+
     @Override
     public void processStatsRequest(Channel channel, OFMessage msg) {
         if (msg.getType() != OFType.STATS_REQUEST) {
@@ -187,8 +239,15 @@
         OFStatsReply ofStatsReply = null;
         switch (ofStatsRequest.getStatsType()) {
             case PORT_DESC:
+                List<OFPortDesc> portDescs = new ArrayList<>();
+                Set<Port> ports = ofSwitchService.ports(networkId, deviceId);
+                ports.forEach(port -> {
+                    OFPortDesc ofPortDesc = portDesc(port);
+                    portDescs.add(ofPortDesc);
+                });
                 ofStatsReply = FACTORY.buildPortDescStatsReply()
                         .setXid(msg.getXid())
+                        .setEntries(portDescs)
                         //TODO add details
                         .build();
                 break;
diff --git a/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/OFSwitchManager.java b/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/OFSwitchManager.java
index e23cb81..59a2b2c 100644
--- a/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/OFSwitchManager.java
+++ b/apps/ofagent/src/main/java/org/onosproject/ofagent/impl/OFSwitchManager.java
@@ -33,8 +33,10 @@
 import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
 import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
 import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -157,10 +159,19 @@
         return ImmutableSet.copyOf(ofSwitches);
     }
 
+    @Override
+    public Set<Port> ports(NetworkId networkId, DeviceId deviceId) {
+        Set<Port> ports = virtualNetService.getVirtualPorts(networkId, deviceId)
+                .stream()
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(ports);
+    }
+
     private void addOFSwitch(NetworkId networkId, DeviceId deviceId) {
         OFSwitch ofSwitch = DefaultOFSwitch.of(
                 dpidWithDeviceId(deviceId),
-                DEFAULT_CAPABILITIES);
+                DEFAULT_CAPABILITIES, networkId, deviceId,
+                virtualNetService.getServiceDirectory());
         ofSwitchMap.put(deviceId, ofSwitch);
         log.info("Added virtual OF switch for {}", deviceId);
 
@@ -289,6 +300,7 @@
 
         @Override
         public void event(VirtualNetworkEvent event) {
+            log.trace("Vnet event {}", event);
             switch (event.type()) {
                 case VIRTUAL_DEVICE_ADDED:
                     eventExecutor.execute(() -> {
@@ -312,14 +324,46 @@
                 case NETWORK_UPDATED:
                 case NETWORK_REMOVED:
                 case NETWORK_ADDED:
+                    break;
                 case VIRTUAL_PORT_ADDED:
+                    eventExecutor.execute(() -> {
+                        OFSwitch ofSwitch = ofSwitch(event.virtualPort());
+                        if (ofSwitch != null) {
+                            ofSwitch.processPortAdded(event.virtualPort());
+                            log.debug("Virtual port {} added to network {}",
+                                      event.virtualPort(),
+                                      event.subject());
+                        }
+                    });
+                    break;
                 case VIRTUAL_PORT_UPDATED:
+                    break;
                 case VIRTUAL_PORT_REMOVED:
+                    eventExecutor.execute(() -> {
+                        OFSwitch ofSwitch = ofSwitch(event.virtualPort());
+                        if (ofSwitch != null) {
+                            ofSwitch.processPortRemoved(event.virtualPort());
+                            log.debug("Virtual port {} removed from network {}",
+                                      event.virtualPort(),
+                                      event.subject());
+                        }
+                    });
+                    break;
                 default:
                     // do nothing
                     break;
             }
         }
+
+        private OFSwitch ofSwitch(VirtualPort virtualPort) {
+            OFSwitch ofSwitch = ofSwitchMap.get(virtualPort.element().id());
+            if (ofSwitch == null) {
+                log.warn("Switch does not exist for port {}", virtualPort);
+            } else {
+                log.trace("Switch exists for port {}", virtualPort);
+            }
+            return ofSwitch;
+        }
     }
 
     private class InternalOFAgentListener implements OFAgentListener {