ONOS-5475 OFAgent - Handle OFFlowStatsRequest, OFTableDescStatsRequest, OFGroupStatsRequest, OFGroupDescStatsRequest

Change-Id: I67734951a756ea61d8aaf9d520b3101141e3d73c
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 395f08e..cbed3d9 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
@@ -21,6 +21,9 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.group.Group;
 
 import java.util.List;
 import java.util.Set;
@@ -73,6 +76,33 @@
     List<PortStatistics> getPortStatistics(NetworkId networkId, DeviceId deviceId);
 
     /**
+     * Returns all flow entries of the specified device in the specified network.
+     *
+     * @param networkId network id
+     * @param deviceId device id
+     * @return list of flow entries; empty list if none exists for the specified device
+     */
+    List<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId);
+
+    /**
+     * Returns all flow table statistics of the specified device in the specified network.
+     *
+     * @param networkId network id
+     * @param deviceId device id
+     * @return list of flow table statistics; empty list if none exists for the specified device
+     */
+    List<TableStatisticsEntry> getFlowTableStatistics(NetworkId networkId, DeviceId deviceId);
+
+    /**
+     * Returns all groups associated with the specified device in the specified network.
+     *
+     * @param networkId network id
+     * @param deviceId device id
+     * @return list of  groups; empty list if none exists for the specified device
+     */
+    List<Group> getGroups(NetworkId networkId, DeviceId deviceId);
+
+    /**
      * Returns neighbour port of the specified port in the specified network.
      *
      * @param networkId network id
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 72a0c2f..3f4f878 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
@@ -16,28 +16,38 @@
 package org.onosproject.ofagent.impl;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import io.netty.channel.Channel;
 import org.onlab.osgi.ServiceDirectory;
 import org.onosproject.incubator.net.virtual.NetworkId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
-import org.onosproject.net.device.PortStatistics;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.group.Group;
+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.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
+import org.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFBucketCounter;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFEchoReply;
 import org.projectfloodlight.openflow.protocol.OFEchoRequest;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFHello;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
@@ -54,14 +64,18 @@
 import org.projectfloodlight.openflow.protocol.OFSetConfig;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.types.U64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -259,6 +273,76 @@
         return ofPortStatsEntry;
     }
 
+    private OFFlowStatsEntry ofFlowStatsEntry(FlowEntry flowEntry) {
+        // TODO get match from flowEntry.selector()
+        Match.Builder matchB = FACTORY.buildMatch();
+        OFActionOutput actionOutput = FACTORY.actions()
+                .buildOutput().build();
+        // TODO get instructions from flowEntry.treatment()
+        OFInstruction instruction = FACTORY.instructions()
+                .applyActions(Collections.singletonList(actionOutput));
+        OFFlowStatsEntry ofFlowStatsEntry = FACTORY.buildFlowStatsEntry()
+                .setMatch(matchB.build())
+                .setInstructions(Collections.singletonList(instruction))
+                .setTableId(TableId.of(flowEntry.tableId()))
+                .setHardTimeout(flowEntry.hardTimeout())
+                .setIdleTimeout(flowEntry.timeout())
+                .setCookie(U64.of(flowEntry.id().value()))
+                .setPriority(flowEntry.priority())
+                .setDurationSec(flowEntry.life())
+                .setPacketCount(U64.of(flowEntry.packets()))
+                .setByteCount(U64.of(flowEntry.bytes()))
+                .build();
+        return ofFlowStatsEntry;
+    }
+
+    private OFTableStatsEntry ofFlowTableStatsEntry(TableStatisticsEntry tableStatisticsEntry) {
+        OFTableStatsEntry ofTableStatsEntry = FACTORY.buildTableStatsEntry()
+                .setTableId(TableId.of(tableStatisticsEntry.tableId()))
+                .setActiveCount(tableStatisticsEntry.activeFlowEntries())
+                .setLookupCount(U64.of(tableStatisticsEntry.packetsLookedup()))
+                .setMatchedCount(U64.of(tableStatisticsEntry.packetsLookedup()))
+                .build();
+        return ofTableStatsEntry;
+    }
+
+    private OFGroupStatsEntry ofGroupStatsEntry(Group group) {
+        List<OFBucketCounter> ofBucketCounters = Lists.newArrayList();
+        group.buckets().buckets().forEach(groupBucket -> {
+            ofBucketCounters.add(FACTORY.bucketCounter(
+                    U64.of(groupBucket.packets()), U64.of(groupBucket.bytes())));
+        });
+        OFGroupStatsEntry entry = FACTORY.buildGroupStatsEntry()
+                .setGroup(OFGroup.of(group.id().id()))
+                .setDurationSec(group.life())
+                .setPacketCount(U64.of(group.packets()))
+                .setByteCount(U64.of(group.bytes()))
+                .setRefCount(group.referenceCount())
+                .setBucketStats(ofBucketCounters)
+                .build();
+        return entry;
+    }
+
+    private OFGroupDescStatsEntry ofGroupDescStatsEntry(Group group) {
+        List<OFBucket> ofBuckets = Lists.newArrayList();
+        group.buckets().buckets().forEach(groupBucket -> {
+            ofBuckets.add(FACTORY.buildBucket()
+                    .setWeight(groupBucket.weight())
+                    .setWatchGroup(OFGroup.of(groupBucket.watchGroup().id()))
+                    .setWatchPort(OFPort.of((int) groupBucket.watchPort().toLong()))
+                    .build()
+            );
+        });
+        OFGroup ofGroup = OFGroup.of(group.givenGroupId());
+        OFGroupType ofGroupType = OFGroupType.valueOf(group.type().name());
+        OFGroupDescStatsEntry entry = FACTORY.buildGroupDescStatsEntry()
+                .setGroup(ofGroup)
+                .setGroupType(ofGroupType)
+                .setBuckets(ofBuckets)
+                .build();
+        return entry;
+    }
+
     @Override
     public void processStatsRequest(Channel channel, OFMessage msg) {
         if (msg.getType() != OFType.STATS_REQUEST) {
@@ -308,6 +392,54 @@
                         //TODO add details
                         .build();
                 break;
+            case FLOW:
+                List<OFFlowStatsEntry> flowStatsEntries = new ArrayList<>();
+                List<FlowEntry> flowStats = ofSwitchService.getFlowEntries(networkId, deviceId);
+                flowStats.forEach(flowEntry -> {
+                    OFFlowStatsEntry ofFlowStatsEntry = ofFlowStatsEntry(flowEntry);
+                    flowStatsEntries.add(ofFlowStatsEntry);
+                });
+                ofStatsReply = FACTORY.buildFlowStatsReply()
+                        .setEntries(flowStatsEntries)
+                        .setXid(msg.getXid())
+                        .build();
+                break;
+            case TABLE:
+                List<OFTableStatsEntry> ofTableStatsEntries = new ArrayList<>();
+                List<TableStatisticsEntry> tableStats = ofSwitchService.getFlowTableStatistics(networkId, deviceId);
+                tableStats.forEach(tableStatisticsEntry -> {
+                    OFTableStatsEntry ofFlowStatsEntry = ofFlowTableStatsEntry(tableStatisticsEntry);
+                    ofTableStatsEntries.add(ofFlowStatsEntry);
+                });
+                ofStatsReply = FACTORY.buildTableStatsReply()
+                        .setEntries(ofTableStatsEntries)
+                        .setXid(msg.getXid())
+                        .build();
+                break;
+            case GROUP:
+                List<Group> groupStats = ofSwitchService.getGroups(networkId, deviceId);
+                List<OFGroupStatsEntry> ofGroupStatsEntries = new ArrayList<>();
+                groupStats.forEach(group -> {
+                    OFGroupStatsEntry entry = ofGroupStatsEntry(group);
+                    ofGroupStatsEntries.add(entry);
+                });
+                ofStatsReply = FACTORY.buildGroupStatsReply()
+                        .setEntries(ofGroupStatsEntries)
+                        .setXid(msg.getXid())
+                        .build();
+                break;
+            case GROUP_DESC:
+                List<OFGroupDescStatsEntry> ofGroupDescStatsEntries = new ArrayList<>();
+                List<Group> groupStats2 = ofSwitchService.getGroups(networkId, deviceId);
+                groupStats2.forEach(group -> {
+                    OFGroupDescStatsEntry entry = ofGroupDescStatsEntry(group);
+                    ofGroupDescStatsEntries.add(entry);
+                });
+                ofStatsReply = FACTORY.buildGroupDescStatsReply()
+                        .setEntries(ofGroupDescStatsEntries)
+                        .setXid(msg.getXid())
+                        .build();
+                break;
             case DESC:
                 ofStatsReply = FACTORY.buildDescStatsReply()
                         .setXid(msg.getXid())
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 101e531..69ea60a 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
@@ -16,6 +16,7 @@
 package org.onosproject.ofagent.impl;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import io.netty.channel.ChannelOutboundInvoker;
 import io.netty.channel.nio.NioEventLoopGroup;
 import org.apache.felix.scr.annotations.Activate;
@@ -44,9 +45,13 @@
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRuleEvent;
 import org.onosproject.net.flow.FlowRuleListener;
 import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupService;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
@@ -65,6 +70,7 @@
 
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -198,6 +204,30 @@
         return null;
     }
 
+    @Override
+    public List<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId) {
+        FlowRuleService flowRuleService = virtualNetService.get(networkId, FlowRuleService.class);
+        Iterable<FlowEntry> entries = flowRuleService.getFlowEntries(deviceId);
+        return Lists.newArrayList(entries);
+    }
+
+    @Override
+    public List<TableStatisticsEntry> getFlowTableStatistics(NetworkId networkId, DeviceId deviceId) {
+        FlowRuleService flowRuleService = virtualNetService.get(networkId, FlowRuleService.class);
+        Iterable<TableStatisticsEntry> entries = flowRuleService.getFlowTableStatistics(deviceId);
+        if (entries == null) {
+            entries = new ArrayList<>();
+        }
+        return Lists.newArrayList(entries);
+    }
+
+    @Override
+    public List<Group> getGroups(NetworkId networkId, DeviceId deviceId) {
+        GroupService groupService = virtualNetService.get(networkId, GroupService.class);
+        Iterable<Group> entries = groupService.getGroups(deviceId);
+        return Lists.newArrayList(entries);
+    }
+
     private void addOFSwitch(NetworkId networkId, DeviceId deviceId) {
         OFSwitch ofSwitch = DefaultOFSwitch.of(
                 dpidWithDeviceId(deviceId),