ONOS-6096 initial impl of dist. virtual packet store; virtual PacketRequest CLI; PacketRequest codec

Change-Id: Iea0a159a977701685c4487e806b26c85a1fcc1a5
diff --git a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkPacketRequestCommand.java b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkPacketRequestCommand.java
new file mode 100644
index 0000000..4cd1c98
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkPacketRequestCommand.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cli.net.vnet;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.EthType;
+import org.onosproject.cli.net.ExtHeader;
+import org.onosproject.cli.net.Icmp6Code;
+import org.onosproject.cli.net.Icmp6Type;
+import org.onosproject.cli.net.IpProtocol;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketService;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Tests virtual network packet requests.
+ */
+@Command(scope = "onos", name = "vnet-packet",
+        description = "Tests virtual network packet requests")
+public class VirtualNetworkPacketRequestCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "command",
+            description = "Command name (requestPackets|getRequests|cancelPackets)",
+            required = true, multiValued = false)
+    private String command = null;
+
+    @Argument(index = 1, name = "networkId", description = "Network ID",
+            required = true, multiValued = false)
+    private Long networkId = null;
+
+    @Option(name = "--deviceId", description = "Device ID",
+            required = false, multiValued = false)
+    private String deviceIdString = null;
+
+    // Traffic selector
+    @Option(name = "-s", aliases = "--ethSrc", description = "Source MAC Address",
+            required = false, multiValued = false)
+    private String srcMacString = null;
+
+    @Option(name = "-d", aliases = "--ethDst", description = "Destination MAC Address",
+            required = false, multiValued = false)
+    private String dstMacString = null;
+
+    @Option(name = "-t", aliases = "--ethType", description = "Ethernet Type",
+            required = false, multiValued = false)
+    private String ethTypeString = null;
+
+    @Option(name = "-v", aliases = "--vlan", description = "VLAN ID",
+            required = false, multiValued = false)
+    private String vlanString = null;
+
+    @Option(name = "--ipProto", description = "IP Protocol",
+            required = false, multiValued = false)
+    private String ipProtoString = null;
+
+    @Option(name = "--ipSrc", description = "Source IP Prefix",
+            required = false, multiValued = false)
+    private String srcIpString = null;
+
+    @Option(name = "--ipDst", description = "Destination IP Prefix",
+            required = false, multiValued = false)
+    private String dstIpString = null;
+
+    @Option(name = "--fLabel", description = "IPv6 Flow Label",
+            required = false, multiValued = false)
+    private String fLabelString = null;
+
+    @Option(name = "--icmp6Type", description = "ICMPv6 Type",
+            required = false, multiValued = false)
+    private String icmp6TypeString = null;
+
+    @Option(name = "--icmp6Code", description = "ICMPv6 Code",
+            required = false, multiValued = false)
+    private String icmp6CodeString = null;
+
+    @Option(name = "--ndTarget", description = "IPv6 Neighbor Discovery Target Address",
+            required = false, multiValued = false)
+    private String ndTargetString = null;
+
+    @Option(name = "--ndSLL", description = "IPv6 Neighbor Discovery Source Link-Layer",
+            required = false, multiValued = false)
+    private String ndSllString = null;
+
+    @Option(name = "--ndTLL", description = "IPv6 Neighbor Discovery Target Link-Layer",
+            required = false, multiValued = false)
+    private String ndTllString = null;
+
+    @Option(name = "--tcpSrc", description = "Source TCP Port",
+            required = false, multiValued = false)
+    private String srcTcpString = null;
+
+    @Option(name = "--tcpDst", description = "Destination TCP Port",
+            required = false, multiValued = false)
+    private String dstTcpString = null;
+
+    @Option(name = "--extHdr", description = "IPv6 Extension Header Pseudo-field",
+            required = false, multiValued = true)
+    private List<String> extHdrStringList = null;
+
+    @Override
+    protected void execute() {
+        VirtualNetworkService service = get(VirtualNetworkService.class);
+        PacketService virtualPacketService = service.get(NetworkId.networkId(networkId), PacketService.class);
+
+        if (command == null) {
+            print("Command is not defined");
+            return;
+        }
+
+        if (command.equals("getRequests")) {
+            getRequests(virtualPacketService);
+            return;
+        }
+
+        TrafficSelector selector = buildTrafficSelector();
+        PacketPriority packetPriority = PacketPriority.CONTROL; //TODO allow user to specify
+        Optional<DeviceId> optionalDeviceId = null;
+        if (!isNullOrEmpty(deviceIdString)) {
+            optionalDeviceId = Optional.of(DeviceId.deviceId(deviceIdString));
+        }
+
+        if (command.equals("requestPackets")) {
+            if (optionalDeviceId != null) {
+                virtualPacketService.requestPackets(selector, packetPriority, appId(), optionalDeviceId);
+            } else {
+                virtualPacketService.requestPackets(selector, packetPriority, appId());
+            }
+            print("Virtual packet requested:\n%s", selector);
+            return;
+        }
+
+       if (command.equals("cancelPackets")) {
+            if (optionalDeviceId != null) {
+                virtualPacketService.cancelPackets(selector, packetPriority, appId(), optionalDeviceId);
+            } else {
+                virtualPacketService.cancelPackets(selector, packetPriority, appId());
+            }
+            print("Virtual packet cancelled:\n%s", selector);
+            return;
+        }
+
+        print("Unsupported command %s", command);
+    }
+
+    private void getRequests(PacketService packetService) {
+        List<PacketRequest> packetRequests = packetService.getRequests();
+        if (outputJson()) {
+            print("%s", json(packetRequests));
+        } else {
+            packetRequests.forEach(packetRequest -> print(packetRequest.toString()));
+        }
+    }
+
+    private JsonNode json(List<PacketRequest> packetRequests) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+        packetRequests.forEach(packetRequest ->
+                                       result.add(jsonForEntity(packetRequest, PacketRequest.class)));
+        return result;
+    }
+
+    /**
+     * Constructs a traffic selector based on the command line arguments
+     * presented to the command.
+     * @return traffic selector
+     */
+    private TrafficSelector buildTrafficSelector() {
+        IpPrefix srcIpPrefix = null;
+        IpPrefix dstIpPrefix = null;
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+        if (!isNullOrEmpty(srcIpString)) {
+            srcIpPrefix = IpPrefix.valueOf(srcIpString);
+            if (srcIpPrefix.isIp4()) {
+                selectorBuilder.matchIPSrc(srcIpPrefix);
+            } else {
+                selectorBuilder.matchIPv6Src(srcIpPrefix);
+            }
+        }
+
+        if (!isNullOrEmpty(dstIpString)) {
+            dstIpPrefix = IpPrefix.valueOf(dstIpString);
+            if (dstIpPrefix.isIp4()) {
+                selectorBuilder.matchIPDst(dstIpPrefix);
+            } else {
+                selectorBuilder.matchIPv6Dst(dstIpPrefix);
+            }
+        }
+
+        if ((srcIpPrefix != null) && (dstIpPrefix != null) &&
+            (srcIpPrefix.version() != dstIpPrefix.version())) {
+            // ERROR: IP src/dst version mismatch
+            throw new IllegalArgumentException(
+                        "IP source and destination version mismatch");
+        }
+
+        //
+        // Set the default EthType based on the IP version if the matching
+        // source or destination IP prefixes.
+        //
+        Short ethType = null;
+        if ((srcIpPrefix != null) && srcIpPrefix.isIp6()) {
+            ethType = EthType.IPV6.value();
+        }
+        if ((dstIpPrefix != null) && dstIpPrefix.isIp6()) {
+            ethType = EthType.IPV6.value();
+        }
+        if (!isNullOrEmpty(ethTypeString)) {
+            ethType = EthType.parseFromString(ethTypeString);
+        }
+        if (ethType != null) {
+            selectorBuilder.matchEthType(ethType);
+        }
+        if (!isNullOrEmpty(vlanString)) {
+            selectorBuilder.matchVlanId(VlanId.vlanId(Short.parseShort(vlanString)));
+        }
+        if (!isNullOrEmpty(srcMacString)) {
+            selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMacString));
+        }
+
+        if (!isNullOrEmpty(dstMacString)) {
+            selectorBuilder.matchEthDst(MacAddress.valueOf(dstMacString));
+        }
+
+        if (!isNullOrEmpty(ipProtoString)) {
+            short ipProtoShort = IpProtocol.parseFromString(ipProtoString);
+            selectorBuilder.matchIPProtocol((byte) ipProtoShort);
+        }
+
+        if (!isNullOrEmpty(fLabelString)) {
+            selectorBuilder.matchIPv6FlowLabel(Integer.parseInt(fLabelString));
+        }
+
+        if (!isNullOrEmpty(icmp6TypeString)) {
+            byte icmp6Type = Icmp6Type.parseFromString(icmp6TypeString);
+            selectorBuilder.matchIcmpv6Type(icmp6Type);
+        }
+
+        if (!isNullOrEmpty(icmp6CodeString)) {
+            byte icmp6Code = Icmp6Code.parseFromString(icmp6CodeString);
+            selectorBuilder.matchIcmpv6Code(icmp6Code);
+        }
+
+        if (!isNullOrEmpty(ndTargetString)) {
+            selectorBuilder.matchIPv6NDTargetAddress(Ip6Address.valueOf(ndTargetString));
+        }
+
+        if (!isNullOrEmpty(ndSllString)) {
+            selectorBuilder.matchIPv6NDSourceLinkLayerAddress(MacAddress.valueOf(ndSllString));
+        }
+
+        if (!isNullOrEmpty(ndTllString)) {
+            selectorBuilder.matchIPv6NDTargetLinkLayerAddress(MacAddress.valueOf(ndTllString));
+        }
+
+        if (!isNullOrEmpty(srcTcpString)) {
+            selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpString)));
+        }
+
+        if (!isNullOrEmpty(dstTcpString)) {
+            selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpString)));
+        }
+
+        if (extHdrStringList != null) {
+            short extHdr = 0;
+            for (String extHdrString : extHdrStringList) {
+                extHdr = (short) (extHdr | ExtHeader.parseFromString(extHdrString));
+            }
+            selectorBuilder.matchIPv6ExthdrFlags(extHdr);
+        }
+
+        return selectorBuilder.build();
+    }
+}