Detangling incubator: virtual nets, tunnels, resource labels, oh my
- virtual networking moved to /apps/virtual; with CLI & REST API
- tunnels and labels moved to /apps/tunnel; with CLI & REST API; UI disabled for now
- protobuf/models moved to /core/protobuf/models
- defunct grpc/rpc registry stuff left under /graveyard
- compile dependencies on /incubator moved to respective modules for compilation
- run-time dependencies will need to be re-tested for dependent apps
- /graveyard will be removed in not-too-distant future
Change-Id: I0a0b995c635487edcf95a352f50dd162186b0b39
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantAddCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantAddCommand.java
new file mode 100644
index 0000000..a449afb
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantAddCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+/**
+ * Creates a new virtual network tenant.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-add-tenant",
+ description = "Creates a new virtual network tenant.")
+
+public class TenantAddCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Tenant ID",
+ required = true, multiValued = false)
+ String id = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.registerTenantId(TenantId.tenantId(id));
+ print("Tenant successfully added.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantCompleter.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantCompleter.java
new file mode 100644
index 0000000..29a625c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantCompleter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Tenant Id completer.
+ */
+@Service
+public class TenantCompleter implements Completer {
+ @Override
+ public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ VirtualNetworkAdminService service = AbstractShellCommand.get(VirtualNetworkAdminService.class);
+
+ SortedSet<String> strings = delegate.getStrings();
+
+ for (TenantId tenantId : service.getTenantIds()) {
+ strings.add(tenantId.id());
+ }
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(session, commandLine, candidates);
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantListCommand.java
new file mode 100644
index 0000000..6590090
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantListCommand.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.utils.Comparators;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lists all tenants.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-tenants",
+ description = "Lists all virtual network tenants.")
+public class TenantListCommand extends AbstractShellCommand {
+
+ private static final String FMT_TENANT = "tenantId=%s";
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ List<TenantId> tenants = new ArrayList<>();
+ tenants.addAll(service.getTenantIds());
+ Collections.sort(tenants, Comparators.TENANT_ID_COMPARATOR);
+
+ tenants.forEach(this::printTenant);
+ }
+
+ private void printTenant(TenantId tenantId) {
+ print(FMT_TENANT, tenantId.id());
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantRemoveCommand.java
new file mode 100644
index 0000000..2877f59
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/TenantRemoveCommand.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+/**
+ * Creates a new virtual network tenant.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-remove-tenant",
+ description = "Removes a virtual network tenant.")
+
+public class TenantRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Tenant ID",
+ required = true, multiValued = false)
+ @Completion(TenantCompleter.class)
+ String id = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.unregisterTenantId(TenantId.tenantId(id));
+ print("Tenant successfully removed.");
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCompleter.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCompleter.java
new file mode 100644
index 0000000..410b483
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCompleter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import static org.onlab.osgi.DefaultServiceDirectory.getService;
+
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Virtual device completer.
+ *
+ * Assumes the first argument which can be parsed to a number is network id.
+ */
+@Service
+public class VirtualDeviceCompleter extends AbstractChoicesCompleter {
+ @Override
+ protected List<String> choices() {
+ //parse argument list for network id
+ String[] argsArray = commandLine.getArguments();
+ for (String str : argsArray) {
+ if (str.matches("[0-9]+")) {
+ long networkId = Long.valueOf(str);
+ return getSortedVirtualDevices(networkId).stream()
+ .map(virtualDevice -> virtualDevice.id().toString())
+ .collect(Collectors.toList());
+ }
+ }
+ return Collections.singletonList("Missing network id");
+ }
+
+ /**
+ * Returns the list of virtual devices sorted using the network identifier.
+ *
+ * @param networkId network id
+ * @return sorted virtual device list
+ */
+ private List<VirtualDevice> getSortedVirtualDevices(long networkId) {
+ VirtualNetworkService service = getService(VirtualNetworkService.class);
+
+ List<VirtualDevice> virtualDevices = new ArrayList<>();
+ virtualDevices.addAll(service.getVirtualDevices(NetworkId.networkId(networkId)));
+ Collections.sort(virtualDevices, Comparators.VIRTUAL_DEVICE_COMPARATOR);
+ return virtualDevices;
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCreateCommand.java
new file mode 100644
index 0000000..187c0e1
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceCreateCommand.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Creates a new virtual device.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-create-device",
+ description = "Creates a new virtual device in a network.")
+public class VirtualDeviceCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Device ID",
+ required = true, multiValued = false)
+ String deviceId = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.createVirtualDevice(NetworkId.networkId(networkId), DeviceId.deviceId(deviceId));
+ print("Virtual device successfully created.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceListCommand.java
new file mode 100644
index 0000000..4a4acfc
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceListCommand.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lists all virtual devices for the network ID.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-devices",
+ description = "Lists all virtual devices in a virtual network.")
+public class VirtualDeviceListCommand extends AbstractShellCommand {
+
+ private static final String FMT_VIRTUAL_DEVICE =
+ "deviceId=%s";
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Override
+ protected void doExecute() {
+
+ getSortedVirtualDevices().forEach(this::printVirtualDevice);
+ }
+
+ /**
+ * Returns the list of virtual devices sorted using the device identifier.
+ *
+ * @return sorted virtual device list
+ */
+ private List<VirtualDevice> getSortedVirtualDevices() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+
+ List<VirtualDevice> virtualDevices = new ArrayList<>();
+ virtualDevices.addAll(service.getVirtualDevices(NetworkId.networkId(networkId)));
+ Collections.sort(virtualDevices, Comparators.VIRTUAL_DEVICE_COMPARATOR);
+ return virtualDevices;
+ }
+
+ /**
+ * Prints out each virtual device.
+ *
+ * @param virtualDevice virtual device
+ */
+ private void printVirtualDevice(VirtualDevice virtualDevice) {
+ print(FMT_VIRTUAL_DEVICE, virtualDevice.id());
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceRemoveCommand.java
new file mode 100644
index 0000000..f6213c7
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualDeviceRemoveCommand.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Removes a virtual device.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-remove-device",
+ description = "Removes a virtual device.")
+public class VirtualDeviceRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Device ID",
+ required = true, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ String deviceId = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.removeVirtualDevice(NetworkId.networkId(networkId), DeviceId.deviceId(deviceId));
+ print("Virtual device successfully removed.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualFlowsListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualFlowsListCommand.java
new file mode 100644
index 0000000..05da804
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualFlowsListCommand.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.Option;
+import org.onlab.util.StringFilter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.PlaceholderCompleter;
+import org.onosproject.cli.net.FlowRuleStatusCompleter;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowEntry.FlowEntryState;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.utils.Comparators;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+
+/**
+ * Lists all currently-known flows.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-flows",
+ description = "Lists all currently-known flows for a virtual network.")
+public class VirtualFlowsListCommand extends AbstractShellCommand {
+
+ private static final Predicate<FlowEntry> TRUE_PREDICATE = f -> true;
+
+ public static final String ANY = "any";
+
+ private static final String LONG_FORMAT = " id=%s, state=%s, bytes=%s, "
+ + "packets=%s, duration=%s, liveType=%s, priority=%s, tableId=%s, appId=%s, "
+ + "payLoad=%s, selector=%s, treatment=%s";
+
+ private static final String SHORT_FORMAT = " %s, bytes=%s, packets=%s, "
+ + "table=%s, priority=%s, selector=%s, treatment=%s";
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "state", description = "Flow Rule state",
+ required = false, multiValued = false)
+ @Completion(FlowRuleStatusCompleter.class)
+ String state = null;
+
+ @Argument(index = 2, name = "uri", description = "Device ID",
+ required = false, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String uri = null;
+
+ @Argument(index = 3, name = "table", description = "Table ID",
+ required = false, multiValued = false)
+ @Completion(PlaceholderCompleter.class)
+ String table = null;
+
+ @Option(name = "-s", aliases = "--short",
+ description = "Print more succinct output for each flow",
+ required = false, multiValued = false)
+ private boolean shortOutput = false;
+
+ @Option(name = "-c", aliases = "--count",
+ description = "Print flow count only",
+ required = false, multiValued = false)
+ private boolean countOnly = false;
+
+ @Option(name = "-f", aliases = "--filter",
+ description = "Filter flows by specific key",
+ required = false, multiValued = true)
+ private List<String> filter = new ArrayList<>();
+
+ private Predicate<FlowEntry> predicate = TRUE_PREDICATE;
+
+ private StringFilter contentFilter;
+
+ @Override
+ protected void doExecute() {
+ CoreService coreService = get(CoreService.class);
+
+ VirtualNetworkService vnetservice = get(VirtualNetworkService.class);
+ DeviceService deviceService = vnetservice.get(NetworkId.networkId(networkId),
+ DeviceService.class);
+ FlowRuleService service = vnetservice.get(NetworkId.networkId(networkId),
+ FlowRuleService.class);
+ contentFilter = new StringFilter(filter, StringFilter.Strategy.AND);
+
+ compilePredicate();
+
+ SortedMap<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
+
+ if (outputJson()) {
+ print("%s", json(flows.keySet(), flows));
+ } else {
+ flows.forEach((device, flow) -> printFlows(device, flow, coreService));
+ }
+ }
+
+ /**
+ * Produces a JSON array of flows grouped by the each device.
+ *
+ * @param devices collection of devices to group flow by
+ * @param flows collection of flows per each device
+ * @return JSON array
+ */
+ private JsonNode json(Iterable<Device> devices,
+ Map<Device, List<FlowEntry>> flows) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.createArrayNode();
+ for (Device device : devices) {
+ result.add(json(mapper, device, flows.get(device)));
+ }
+ return result;
+ }
+
+ /**
+ * Compiles a predicate to find matching flows based on the command
+ * arguments.
+ */
+ private void compilePredicate() {
+ if (state != null && !state.equals(ANY)) {
+ final FlowEntryState feState = FlowEntryState.valueOf(state.toUpperCase());
+ predicate = predicate.and(f -> f.state().equals(feState));
+ }
+
+ if (table != null) {
+ final int tableId = Integer.parseInt(table);
+ predicate = predicate.and(f -> f.tableId() == tableId);
+ }
+ }
+
+ // Produces JSON object with the flows of the given device.
+ private ObjectNode json(ObjectMapper mapper,
+ Device device, List<FlowEntry> flows) {
+ ObjectNode result = mapper.createObjectNode();
+ ArrayNode array = mapper.createArrayNode();
+
+ flows.forEach(flow -> array.add(jsonForEntity(flow, FlowEntry.class)));
+
+ result.put("device", device.id().toString())
+ .put("flowCount", flows.size())
+ .set("flows", array);
+ return result;
+ }
+
+ /**
+ * Returns the list of devices sorted using the device ID URIs.
+ *
+ * @param deviceService device service
+ * @param service flow rule service
+ * @return sorted device list
+ */
+ protected SortedMap<Device, List<FlowEntry>> getSortedFlows(DeviceService deviceService,
+ FlowRuleService service) {
+ SortedMap<Device, List<FlowEntry>> flows = new TreeMap<>(Comparators.ELEMENT_COMPARATOR);
+ List<FlowEntry> rules;
+
+ Iterable<Device> devices = null;
+ if (uri == null) {
+ devices = deviceService.getDevices();
+ } else {
+ Device dev = deviceService.getDevice(DeviceId.deviceId(uri));
+ devices = (dev == null) ? deviceService.getDevices()
+ : Collections.singletonList(dev);
+ }
+
+ for (Device d : devices) {
+ if (predicate.equals(TRUE_PREDICATE)) {
+ rules = newArrayList(service.getFlowEntries(d.id()));
+ } else {
+ rules = newArrayList();
+ for (FlowEntry f : service.getFlowEntries(d.id())) {
+ if (predicate.test(f)) {
+ rules.add(f);
+ }
+ }
+ }
+ rules.sort(Comparators.FLOW_RULE_COMPARATOR);
+
+ flows.put(d, rules);
+ }
+ return flows;
+ }
+
+ /**
+ * Prints flows.
+ *
+ * @param d the device
+ * @param flows the set of flows for that device
+ * @param coreService core system service
+ */
+ protected void printFlows(Device d, List<FlowEntry> flows,
+ CoreService coreService) {
+ List<FlowEntry> filteredFlows = flows.stream().
+ filter(f -> contentFilter.filter(f)).collect(Collectors.toList());
+ boolean empty = filteredFlows == null || filteredFlows.isEmpty();
+ print("deviceId=%s, flowRuleCount=%d", d.id(), empty ? 0 : filteredFlows.size());
+ if (empty || countOnly) {
+ return;
+ }
+
+ for (FlowEntry f : filteredFlows) {
+ if (shortOutput) {
+ print(SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
+ f.tableId(), f.priority(), f.selector().criteria(),
+ printTreatment(f.treatment()));
+ } else {
+ ApplicationId appId = coreService.getAppId(f.appId());
+ print(LONG_FORMAT, Long.toHexString(f.id().value()), f.state(),
+ f.bytes(), f.packets(), f.life(), f.liveType(), f.priority(), f.tableId(),
+ appId != null ? appId.name() : "<none>",
+ f.payLoad() == null ? null : Arrays.toString(f.payLoad().payLoad()),
+ f.selector().criteria(), f.treatment());
+ }
+ }
+ }
+
+ private String printTreatment(TrafficTreatment treatment) {
+ final String delimiter = ", ";
+ StringBuilder builder = new StringBuilder("[");
+ if (!treatment.immediate().isEmpty()) {
+ builder.append("immediate=" + treatment.immediate() + delimiter);
+ }
+ if (!treatment.deferred().isEmpty()) {
+ builder.append("deferred=" + treatment.deferred() + delimiter);
+ }
+ if (treatment.clearedDeferred()) {
+ builder.append("clearDeferred" + delimiter);
+ }
+ if (treatment.tableTransition() != null) {
+ builder.append("transition=" + treatment.tableTransition() + delimiter);
+ }
+ if (treatment.metered() != null) {
+ builder.append("meter=" + treatment.metered() + delimiter);
+ }
+ if (treatment.writeMetadata() != null) {
+ builder.append("metadata=" + treatment.writeMetadata() + delimiter);
+ }
+ // Chop off last delimiter
+ builder.replace(builder.length() - delimiter.length(), builder.length(), "");
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCompleter.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCompleter.java
new file mode 100644
index 0000000..c8f6c44
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCompleter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.onlab.osgi.DefaultServiceDirectory.getService;
+
+/**
+ * Virtual host completer.
+ *
+ * Assumes the first argument which can be parsed to a number is network id.
+ */
+public class VirtualHostCompleter extends AbstractChoicesCompleter {
+ @Override
+ protected List<String> choices() {
+ //parse argument list for network id
+ String[] argsArray = commandLine.getArguments();
+ for (String str : argsArray) {
+ if (str.matches("[0-9]+")) {
+ long networkId = Long.valueOf(str);
+ return getSortedVirtualHosts(networkId).stream()
+ .map(virtualHost -> virtualHost.id().toString())
+ .collect(Collectors.toList());
+ }
+ }
+ return Collections.singletonList("Missing network id");
+ }
+
+ /**
+ * Returns the list of virtual hosts sorted using the host identifier.
+ *
+ * @param networkId network id
+ * @return virtual host list
+ */
+ private List<VirtualHost> getSortedVirtualHosts(long networkId) {
+ VirtualNetworkService service = getService(VirtualNetworkService.class);
+
+ List<VirtualHost> virtualHosts = new ArrayList<>();
+ virtualHosts.addAll(service.getVirtualHosts(NetworkId.networkId(networkId)));
+ return virtualHosts;
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCreateCommand.java
new file mode 100644
index 0000000..da198b1
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostCreateCommand.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Creates a new virtual host.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-create-host",
+ description = "Creates a new virtual host in a network.")
+public class VirtualHostCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "mac", description = "Mac address",
+ required = true, multiValued = false)
+ String mac = null;
+
+ @Argument(index = 2, name = "vlan", description = "Vlan",
+ required = true, multiValued = false)
+ short vlan;
+
+ @Argument(index = 3, name = "hostLocationDeviceId", description = "Host location device ID",
+ required = true, multiValued = false)
+ String hostLocationDeviceId;
+
+ @Argument(index = 4, name = "hostLocationPortNumber", description = "Host location port number",
+ required = true, multiValued = false)
+ long hostLocationPortNumber;
+
+ // ip addresses
+ @Option(name = "--hostIp", description = "Host IP addresses. Can be specified multiple times.",
+ required = false, multiValued = true)
+ protected String[] hostIpStrings;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+
+ Set<IpAddress> hostIps = new HashSet<>();
+ if (hostIpStrings != null) {
+ Arrays.stream(hostIpStrings).forEach(s -> hostIps.add(IpAddress.valueOf(s)));
+ }
+ HostLocation hostLocation = new HostLocation(DeviceId.deviceId(hostLocationDeviceId),
+ PortNumber.portNumber(hostLocationPortNumber),
+ System.currentTimeMillis());
+ MacAddress macAddress = MacAddress.valueOf(mac);
+ VlanId vlanId = VlanId.vlanId(vlan);
+ service.createVirtualHost(NetworkId.networkId(networkId),
+ HostId.hostId(macAddress, vlanId), macAddress, vlanId,
+ hostLocation, hostIps);
+ print("Virtual host successfully created.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostListCommand.java
new file mode 100644
index 0000000..38533ee
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostListCommand.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Lists all virtual hosts for the network ID.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-hosts",
+ description = "Lists all virtual hosts in a virtual network.")
+public class VirtualHostListCommand extends AbstractShellCommand {
+
+ private static final String FMT_VIRTUAL_HOST =
+ "id=%s, mac=%s, vlan=%s, location=%s, ips=%s";
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Override
+ protected void doExecute() {
+ getSortedVirtualHosts().forEach(this::printVirtualHost);
+ }
+
+ /**
+ * Returns the list of virtual hosts sorted using the device identifier.
+ *
+ * @return virtual host list
+ */
+ private List<VirtualHost> getSortedVirtualHosts() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+
+ List<VirtualHost> virtualHosts = new ArrayList<>();
+ virtualHosts.addAll(service.getVirtualHosts(NetworkId.networkId(networkId)));
+ return virtualHosts;
+ }
+
+ /**
+ * Prints out each virtual host.
+ *
+ * @param virtualHost virtual host
+ */
+ private void printVirtualHost(VirtualHost virtualHost) {
+ print(FMT_VIRTUAL_HOST, virtualHost.id().toString(), virtualHost.mac().toString(),
+ virtualHost.vlan().toString(), virtualHost.location().toString(),
+ virtualHost.ipAddresses().toString());
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostRemoveCommand.java
new file mode 100644
index 0000000..329e4a3
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualHostRemoveCommand.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.HostId;
+//import org.onosproject.net.HostId;
+
+/**
+ * Removes a virtual host.
+ */
+
+@Service
+@Command(scope = "onos", name = "vnet-remove-host",
+ description = "Removes a virtual host.")
+public class VirtualHostRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "id", description = "Host ID",
+ required = true, multiValued = false)
+ @Completion(VirtualHostCompleter.class)
+ String id = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.removeVirtualHost(NetworkId.networkId(networkId), HostId.hostId(id));
+ print("Virtual host successfully removed.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkCreateCommand.java
new file mode 100644
index 0000000..4bc6ea5
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkCreateCommand.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Creates a new virtual link.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-create-link",
+ description = "Creates a new virtual link in a network.")
+public class VirtualLinkCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "srcDeviceId", description = "Source device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String srcDeviceId = null;
+
+ @Argument(index = 2, name = "srcPortNum", description = "Source port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer srcPortNum = null;
+
+ @Argument(index = 3, name = "dstDeviceId", description = "Destination device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String dstDeviceId = null;
+
+ @Argument(index = 4, name = "dstPortNum", description = "Destination port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer dstPortNum = null;
+
+ @Option(name = "-b", aliases = "--bidirectional",
+ description = "If this argument is passed in then the virtual link created will be bidirectional, " +
+ "otherwise the link will be unidirectional.",
+ required = false, multiValued = false)
+ boolean bidirectional = false;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ ConnectPoint src = new ConnectPoint(DeviceId.deviceId(srcDeviceId), PortNumber.portNumber(srcPortNum));
+ ConnectPoint dst = new ConnectPoint(DeviceId.deviceId(dstDeviceId), PortNumber.portNumber(dstPortNum));
+
+ service.createVirtualLink(NetworkId.networkId(networkId), src, dst);
+ if (bidirectional) {
+ service.createVirtualLink(NetworkId.networkId(networkId), dst, src);
+ }
+ print("Virtual link successfully created.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkListCommand.java
new file mode 100644
index 0000000..a69e0b7
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkListCommand.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Lists all virtual links for the network ID.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-links",
+ description = "Lists all virtual links in a virtual network.")
+public class VirtualLinkListCommand extends AbstractShellCommand {
+
+ private static final String FMT_VIRTUAL_LINK =
+ "src=%s, dst=%s, state=%s, tunnelId=%s";
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Override
+ protected void doExecute() {
+
+ getSortedVirtualLinks().forEach(this::printVirtualLink);
+ }
+
+ /**
+ * Returns the list of virtual links sorted using the device identifier.
+ *
+ * @return virtual link list
+ */
+ private List<VirtualLink> getSortedVirtualLinks() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+
+ List<VirtualLink> virtualLinks = new ArrayList<>();
+ virtualLinks.addAll(service.getVirtualLinks(NetworkId.networkId(networkId)));
+ return virtualLinks;
+ }
+
+ /**
+ * Prints out each virtual link.
+ *
+ * @param virtualLink virtual link
+ */
+ private void printVirtualLink(VirtualLink virtualLink) {
+ print(FMT_VIRTUAL_LINK, virtualLink.src().toString(), virtualLink.dst().toString(),
+ virtualLink.state(),
+ virtualLink.tunnelId() == null ? null : virtualLink.tunnelId().toString());
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkRemoveCommand.java
new file mode 100644
index 0000000..5f613f4
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualLinkRemoveCommand.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Removes a virtual link.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-remove-link",
+ description = "Removes a virtual link.")
+public class VirtualLinkRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "srcDeviceId", description = "Source device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String srcDeviceId = null;
+
+ @Argument(index = 2, name = "srcPortNum", description = "Source port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer srcPortNum = null;
+
+ @Argument(index = 3, name = "dstDeviceId", description = "Destination device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String dstDeviceId = null;
+
+ @Argument(index = 4, name = "dstPortNum", description = "Destination port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer dstPortNum = null;
+
+ @Option(name = "-b", aliases = "--bidirectional",
+ description = "If this argument is passed in then then bidirectional virtual link will be removed, " +
+ "otherwise the unidirectional link will be removed.",
+ required = false, multiValued = false)
+ boolean bidirectional = false;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ ConnectPoint src = new ConnectPoint(DeviceId.deviceId(srcDeviceId), PortNumber.portNumber(srcPortNum));
+ ConnectPoint dst = new ConnectPoint(DeviceId.deviceId(dstDeviceId), PortNumber.portNumber(dstPortNum));
+
+ service.removeVirtualLink(NetworkId.networkId(networkId), src, dst);
+ if (bidirectional) {
+ service.removeVirtualLink(NetworkId.networkId(networkId), dst, src);
+ }
+ print("Virtual link successfully removed.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkBalanceMastersCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkBalanceMastersCommand.java
new file mode 100644
index 0000000..51a0c96
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkBalanceMastersCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.mastership.MastershipAdminService;
+
+/**
+ * Forces virtual network device mastership rebalancing.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-balance-masters",
+ description = "Forces virtual network device mastership rebalancing")
+public class VirtualNetworkBalanceMastersCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+ @Override
+ protected void doExecute() {
+ VirtualNetworkService vnetService = get(VirtualNetworkService.class);
+ MastershipAdminService mastershipAdminService = vnetService
+ .get(NetworkId.networkId(networkId), MastershipAdminService.class);
+ mastershipAdminService.balanceRoles();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCompleter.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCompleter.java
new file mode 100644
index 0000000..1a3e7a2
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCompleter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.SortedSet;
+
+/**
+ * Virtual network completer.
+ */
+@Service
+public class VirtualNetworkCompleter implements Completer {
+ @Override
+ public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+ // Delegate string completer
+ StringsCompleter delegate = new StringsCompleter();
+
+ // Fetch our service and feed it's offerings to the string completer
+ VirtualNetworkAdminService service = AbstractShellCommand.get(VirtualNetworkAdminService.class);
+
+ List<VirtualNetwork> virtualNetworks = new ArrayList<>();
+
+ Set<TenantId> tenantSet = service.getTenantIds();
+ tenantSet.forEach(tenantId -> virtualNetworks.addAll(service.getVirtualNetworks(tenantId)));
+
+ Collections.sort(virtualNetworks, Comparators.VIRTUAL_NETWORK_COMPARATOR);
+
+ SortedSet<String> strings = delegate.getStrings();
+ virtualNetworks.forEach(virtualNetwork -> strings.add(virtualNetwork.id().toString()));
+
+ // Now let the completer do the work for figuring out what to offer.
+ return delegate.complete(session, commandLine, candidates);
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCreateCommand.java
new file mode 100644
index 0000000..d1ebe47
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkCreateCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+/**
+ * Creates a new virtual network.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-create",
+ description = "Creates a new virtual network.")
+public class VirtualNetworkCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Tenant ID",
+ required = true, multiValued = false)
+ @Completion(TenantCompleter.class)
+ String id = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.createVirtualNetwork(TenantId.tenantId(id));
+ print("Virtual network successfully created.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentCreateCommand.java
new file mode 100644
index 0000000..4a8b400
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentCreateCommand.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.net.ConnectivityIntentCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+
+import java.util.List;
+
+/**
+ * Installs virtual network intents.
+ */
+@Service
+@Command(scope = "onos", name = "add-vnet-intent",
+ description = "Installs virtual network connectivity intent")
+public class VirtualNetworkIntentCreateCommand extends ConnectivityIntentCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "ingressDevice",
+ description = "Ingress Device/Port Description",
+ required = true, multiValued = false)
+ String ingressDeviceString = null;
+
+ @Argument(index = 2, name = "egressDevice",
+ description = "Egress Device/Port Description",
+ required = true, multiValued = false)
+ String egressDeviceString = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+ IntentService virtualNetworkIntentService = service.get(NetworkId.networkId(networkId), IntentService.class);
+
+ ConnectPoint ingress = ConnectPoint.deviceConnectPoint(ingressDeviceString);
+ ConnectPoint egress = ConnectPoint.deviceConnectPoint(egressDeviceString);
+
+ TrafficSelector selector = buildTrafficSelector();
+ TrafficTreatment treatment = buildTrafficTreatment();
+
+ List<Constraint> constraints = buildConstraints();
+
+ Intent intent = VirtualNetworkIntent.builder()
+ .networkId(NetworkId.networkId(networkId))
+ .appId(appId())
+ .key(key())
+ .selector(selector)
+ .treatment(treatment)
+ .ingressPoint(ingress)
+ .egressPoint(egress)
+ .constraints(constraints)
+ .priority(priority())
+ .build();
+ virtualNetworkIntentService.submit(intent);
+ print("Virtual intent submitted:\n%s", intent.toString());
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentRemoveCommand.java
new file mode 100644
index 0000000..eea3d45
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkIntentRemoveCommand.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+
+import java.math.BigInteger;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+
+/**
+ * Removes a virtual network intent.
+ */
+@Service
+@Command(scope = "onos", name = "remove-vnet-intent",
+ description = "Removes the virtual network intent")
+public class VirtualNetworkIntentRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "app",
+ description = "Application ID",
+ required = false, multiValued = false)
+ String applicationIdString = null;
+
+ @Argument(index = 2, name = "key",
+ description = "Intent Key",
+ required = false, multiValued = false)
+ String keyString = null;
+
+ @Option(name = "-p", aliases = "--purge",
+ description = "Purge the intent from the store after removal",
+ required = false, multiValued = false)
+ private boolean purgeAfterRemove = false;
+
+ @Option(name = "-s", aliases = "--sync",
+ description = "Waits for the removal before returning",
+ required = false, multiValued = false)
+ private boolean sync = false;
+
+ private static final EnumSet<IntentState> CAN_PURGE = EnumSet.of(WITHDRAWN, FAILED);
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+ IntentService intentService = service.get(NetworkId.networkId(networkId), IntentService.class);
+ CoreService coreService = get(CoreService.class);
+
+ if (purgeAfterRemove || sync) {
+ print("Using \"sync\" to remove/purge intents - this may take a while...");
+ print("Check \"summary\" to see remove/purge progress.");
+ }
+
+ ApplicationId appId = appId();
+ if (!isNullOrEmpty(applicationIdString)) {
+ appId = coreService.getAppId(applicationIdString);
+ if (appId == null) {
+ print("Cannot find application Id %s", applicationIdString);
+ return;
+ }
+ }
+
+ if (isNullOrEmpty(keyString)) {
+ for (Intent intent : intentService.getIntents()) {
+ if (intent.appId().equals(appId)) {
+ removeIntent(intentService, intent);
+ }
+ }
+
+ } else {
+ final Key key;
+ if (keyString.startsWith("0x")) {
+ // The intent uses a LongKey
+ keyString = keyString.replaceFirst("0x", "");
+ key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
+ } else {
+ // The intent uses a StringKey
+ key = Key.of(keyString, appId);
+ }
+
+ Intent intent = intentService.getIntent(key);
+ if (intent != null) {
+ removeIntent(intentService, intent);
+ } else {
+ print("Intent not found!");
+ }
+ }
+ }
+
+ /**
+ * Removes the intent using the specified intentService.
+ *
+ * @param intentService intent service
+ * @param intent intent
+ */
+ private void removeIntent(IntentService intentService, Intent intent) {
+ IntentListener listener = null;
+ Key key = intent.key();
+ final CountDownLatch withdrawLatch, purgeLatch;
+ if (purgeAfterRemove || sync) {
+ // set up latch and listener to track uninstall progress
+ withdrawLatch = new CountDownLatch(1);
+ purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
+ listener = (IntentEvent event) -> {
+ if (Objects.equals(event.subject().key(), key)) {
+ if (event.type() == IntentEvent.Type.WITHDRAWN ||
+ event.type() == IntentEvent.Type.FAILED) {
+ withdrawLatch.countDown();
+ } else if (purgeLatch != null && purgeAfterRemove &&
+ event.type() == IntentEvent.Type.PURGED) {
+ purgeLatch.countDown();
+ }
+ }
+ };
+ intentService.addListener(listener);
+ } else {
+ purgeLatch = null;
+ withdrawLatch = null;
+ }
+
+ // request the withdraw
+ intentService.withdraw(intent);
+
+ if ((purgeAfterRemove || sync) && purgeLatch != null) {
+ try { // wait for withdraw event
+ withdrawLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ print("Timed out waiting for intent {} withdraw", key);
+ }
+ if (purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
+ intentService.purge(intent);
+ if (sync) { // wait for purge event
+ /* TODO
+ Technically, the event comes before map.remove() is called.
+ If we depend on sync and purge working together, we will
+ need to address this.
+ */
+ try {
+ purgeLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ print("Timed out waiting for intent {} purge", key);
+ }
+ }
+ }
+ }
+
+ if (listener != null) {
+ // clean up the listener
+ intentService.removeListener(listener);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkListCommand.java
new file mode 100644
index 0000000..df1172c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkListCommand.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Lists all virtual networks for the tenant ID.
+ */
+@Service
+@Command(scope = "onos", name = "vnets",
+ description = "Lists all virtual networks.")
+public class VirtualNetworkListCommand extends AbstractShellCommand {
+
+ private static final String FMT_VIRTUAL_NETWORK =
+ "tenantId=%s, networkId=%s";
+
+ @Override
+ protected void doExecute() {
+
+ getSortedVirtualNetworks().forEach(this::printVirtualNetwork);
+ }
+
+ /**
+ * Returns the list of virtual networks sorted using the tenant identifier.
+ *
+ * @return sorted virtual network list
+ */
+ private List<VirtualNetwork> getSortedVirtualNetworks() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+ VirtualNetworkAdminService adminService = get(VirtualNetworkAdminService.class);
+
+ List<VirtualNetwork> virtualNetworks = new ArrayList<>();
+
+ Set<TenantId> tenantSet = adminService.getTenantIds();
+ tenantSet.forEach(tenantId -> virtualNetworks.addAll(service.getVirtualNetworks(tenantId)));
+
+ Collections.sort(virtualNetworks, Comparators.VIRTUAL_NETWORK_COMPARATOR);
+ return virtualNetworks;
+ }
+
+ /**
+ * Prints out each virtual network.
+ *
+ * @param virtualNetwork virtual network
+ */
+ private void printVirtualNetwork(VirtualNetwork virtualNetwork) {
+ print(FMT_VIRTUAL_NETWORK, virtualNetwork.tenantId(), virtualNetwork.id());
+ }
+}
+
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkPacketRequestCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkPacketRequestCommand.java
new file mode 100644
index 0000000..059829d
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkPacketRequestCommand.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.action.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.EthTypeCompleter;
+import org.onosproject.cli.net.ExtHeader;
+import org.onosproject.cli.net.ExtHeaderCompleter;
+import org.onosproject.cli.net.Icmp6Code;
+import org.onosproject.cli.net.Icmp6CodeCompleter;
+import org.onosproject.cli.net.Icmp6Type;
+import org.onosproject.cli.net.Icmp6TypeCompleter;
+import org.onosproject.cli.net.IpProtocol;
+import org.onosproject.cli.net.IpProtocolCompleter;
+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.
+ */
+@Service
+@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)
+ @Completion(EthTypeCompleter.class)
+ 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)
+ @Completion(IpProtocolCompleter.class)
+ 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)
+ @Completion(Icmp6TypeCompleter.class)
+ private String icmp6TypeString = null;
+
+ @Option(name = "--icmp6Code", description = "ICMPv6 Code",
+ required = false, multiValued = false)
+ @Completion(Icmp6CodeCompleter.class)
+ 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)
+ @Completion(ExtHeaderCompleter.class)
+ private List<String> extHdrStringList = null;
+
+ @Override
+ protected void doExecute() {
+ 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();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkRemoveCommand.java
new file mode 100644
index 0000000..2b28044
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualNetworkRemoveCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+
+/**
+ * Removes a virtual network.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-remove",
+ description = "Removes a virtual network.")
+public class VirtualNetworkRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "id", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long id;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.removeVirtualNetwork(NetworkId.networkId(id));
+ print("Virtual network successfully removed.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortBindCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortBindCommand.java
new file mode 100644
index 0000000..7013a01
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortBindCommand.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.cli.net.PortNumberCompleter;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Binds an existing virtual port with a physical port.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-bind-port",
+ description = "Binds an existing virtual port with a physical port.")
+public class VirtualPortBindCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Virtual Device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String deviceId = null;
+
+ @Argument(index = 2, name = "portNum", description = "Virtual device port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer portNum = null;
+
+ @Argument(index = 3, name = "physDeviceId", description = "Physical Device ID",
+ required = true, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ String physDeviceId = null;
+
+ @Argument(index = 4, name = "physPortNum", description = "Physical device port number",
+ required = true, multiValued = false)
+ @Completion(PortNumberCompleter.class)
+ Integer physPortNum = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ DeviceService deviceService = get(DeviceService.class);
+
+ VirtualPort vPort = getVirtualPort(PortNumber.portNumber(portNum));
+ checkNotNull(vPort, "The virtual Port does not exist");
+
+ ConnectPoint realizedBy = new ConnectPoint(DeviceId.deviceId(physDeviceId),
+ PortNumber.portNumber(physPortNum));
+ service.bindVirtualPort(NetworkId.networkId(networkId), DeviceId.deviceId(deviceId),
+ PortNumber.portNumber(portNum), realizedBy);
+ print("Virtual port is successfully bound.");
+ }
+
+ /**
+ * Returns the virtual port matching the device and port identifier.
+ *
+ * @param aPortNumber port identifier
+ * @return matching virtual port, or null.
+ */
+ private VirtualPort getVirtualPort(PortNumber aPortNumber) {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+ Set<VirtualPort> ports = service.getVirtualPorts(NetworkId.networkId(networkId),
+ DeviceId.deviceId(deviceId));
+ return ports.stream().filter(p -> p.number().equals(aPortNumber))
+ .findFirst().get();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCompleter.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCompleter.java
new file mode 100644
index 0000000..141c964
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCompleter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import static org.onlab.osgi.DefaultServiceDirectory.getService;
+
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.DeviceId;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+/**
+ * Virtual port completer.
+ *
+ * Assumes the first argument which can be parsed to a number is network id
+ * and the argument right before the one being completed is device id
+ */
+@Service
+public class VirtualPortCompleter extends AbstractChoicesCompleter {
+ @Override
+ protected List<String> choices() {
+ //parse argument list for network id
+ String[] argsArray = commandLine.getArguments();
+ for (String str : argsArray) {
+ if (str.matches("[0-9]+")) {
+ long networkId = Long.valueOf(str);
+ String deviceId = argsArray[argsArray.length - 1];
+ return getSortedVirtualPorts(networkId, deviceId).stream()
+ .map(virtualPort -> virtualPort.number().toString())
+ .collect(Collectors.toList());
+ }
+ }
+
+ return Collections.singletonList("Missing network id");
+ }
+
+ /**
+ * Returns the list of virtual ports sorted using the port number.
+ *
+ * @param networkId network id.
+ * @param deviceId device id
+ * @return sorted virtual port list
+ */
+ private List<VirtualPort> getSortedVirtualPorts(long networkId, String deviceId) {
+ VirtualNetworkService service = getService(VirtualNetworkService.class);
+
+ List<VirtualPort> virtualPorts = new ArrayList<>();
+ virtualPorts.addAll(service.getVirtualPorts(NetworkId.networkId(networkId),
+ DeviceId.deviceId(deviceId)));
+ Collections.sort(virtualPorts, Comparators.VIRTUAL_PORT_COMPARATOR);
+ return virtualPorts;
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCreateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCreateCommand.java
new file mode 100644
index 0000000..a466a64
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortCreateCommand.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.cli.net.PortNumberCompleter;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Creates a new virtual port.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-create-port",
+ description = "Creates a new virtual port in a network.")
+public class VirtualPortCreateCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Virtual Device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String deviceId = null;
+
+ @Argument(index = 2, name = "portNum", description = "Virtual device port number",
+ required = true, multiValued = false)
+ Integer portNum = null;
+
+ @Argument(index = 3, name = "physDeviceId", description = "Physical Device ID",
+ required = false, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ String physDeviceId = null;
+
+ @Argument(index = 4, name = "physPortNum", description = "Physical device port number",
+ required = false, multiValued = false)
+ @Completion(PortNumberCompleter.class)
+ Integer physPortNum = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ DeviceService deviceService = get(DeviceService.class);
+
+ VirtualDevice virtualDevice = getVirtualDevice(DeviceId.deviceId(deviceId));
+ checkNotNull(virtualDevice, "The virtual device does not exist.");
+
+ ConnectPoint realizedBy = null;
+ if (physDeviceId != null && physPortNum != null) {
+ checkNotNull(physPortNum, "The physical port does not specified.");
+ realizedBy = new ConnectPoint(DeviceId.deviceId(physDeviceId),
+ PortNumber.portNumber(physPortNum));
+ checkNotNull(realizedBy, "The physical port does not exist.");
+ }
+
+ service.createVirtualPort(NetworkId.networkId(networkId), DeviceId.deviceId(deviceId),
+ PortNumber.portNumber(portNum), realizedBy);
+ print("Virtual port successfully created.");
+ }
+
+ /**
+ * Returns the virtual device matching the device identifier.
+ *
+ * @param aDeviceId device identifier
+ * @return matching virtual device, or null.
+ */
+ private VirtualDevice getVirtualDevice(DeviceId aDeviceId) {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+
+ Set<VirtualDevice> virtualDevices = service.getVirtualDevices(NetworkId.networkId(networkId));
+
+ for (VirtualDevice virtualDevice : virtualDevices) {
+ if (virtualDevice.id().equals(aDeviceId)) {
+ return virtualDevice;
+ }
+ }
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortListCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortListCommand.java
new file mode 100644
index 0000000..f7a6c08
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortListCommand.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.Comparators;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.DeviceId;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lists all virtual ports for the network ID.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-ports",
+ description = "Lists all virtual ports in a virtual network.")
+public class VirtualPortListCommand extends AbstractShellCommand {
+
+ private static final String FMT_VIRTUAL_PORT =
+ "virtual portNumber=%s, physical deviceId=%s, portNumber=%s, isEnabled=%s";
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Virtual Device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String deviceId = null;
+
+ @Override
+ protected void doExecute() {
+
+ getSortedVirtualPorts().forEach(this::printVirtualPort);
+ }
+
+ /**
+ * Returns the list of virtual ports sorted using the network identifier.
+ *
+ * @return sorted virtual port list
+ */
+ private List<VirtualPort> getSortedVirtualPorts() {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+
+ List<VirtualPort> virtualPorts = new ArrayList<>();
+ virtualPorts.addAll(service.getVirtualPorts(NetworkId.networkId(networkId),
+ DeviceId.deviceId(deviceId)));
+ Collections.sort(virtualPorts, Comparators.VIRTUAL_PORT_COMPARATOR);
+ return virtualPorts;
+ }
+
+ /**
+ * Prints out each virtual port.
+ *
+ * @param virtualPort virtual port
+ */
+ private void printVirtualPort(VirtualPort virtualPort) {
+ if (virtualPort.realizedBy() == null) {
+ print(FMT_VIRTUAL_PORT, virtualPort.number(), "None", "None", virtualPort.isEnabled());
+ } else {
+ print(FMT_VIRTUAL_PORT, virtualPort.number(),
+ virtualPort.realizedBy().deviceId(),
+ virtualPort.realizedBy().port(),
+ virtualPort.isEnabled());
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortRemoveCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortRemoveCommand.java
new file mode 100644
index 0000000..6c79276
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortRemoveCommand.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Removes a virtual port.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-remove-port",
+ description = "Removes a virtual port.")
+public class VirtualPortRemoveCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String deviceId = null;
+
+ @Argument(index = 2, name = "portNum", description = "Device port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer portNum = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+ service.removeVirtualPort(NetworkId.networkId(networkId), DeviceId.deviceId(deviceId),
+ PortNumber.portNumber(portNum));
+ print("Virtual port successfully removed.");
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortStateCommand.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortStateCommand.java
new file mode 100644
index 0000000..56af2ba
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/VirtualPortStateCommand.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Administratively enables or disables state of an existing virtual port.
+ */
+@Service
+@Command(scope = "onos", name = "vnet-port-state",
+ description = "Administratively enables or disables state of an existing virtual port.")
+public class VirtualPortStateCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "networkId", description = "Network ID",
+ required = true, multiValued = false)
+ @Completion(VirtualNetworkCompleter.class)
+ Long networkId = null;
+
+ @Argument(index = 1, name = "deviceId", description = "Virtual Device ID",
+ required = true, multiValued = false)
+ @Completion(VirtualDeviceCompleter.class)
+ String deviceId = null;
+
+ @Argument(index = 2, name = "portNum", description = "Virtual device port number",
+ required = true, multiValued = false)
+ @Completion(VirtualPortCompleter.class)
+ Integer portNum = null;
+
+ @Argument(index = 3, name = "portState",
+ description = "Desired State. Either \"enable\" or \"disable\".",
+ required = true, multiValued = false)
+ String portState = null;
+
+ @Override
+ protected void doExecute() {
+ VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
+
+ VirtualPort vPort = getVirtualPort(PortNumber.portNumber(portNum));
+ checkNotNull(vPort, "The virtual Port does not exist");
+
+ boolean isEnabled;
+ if ("enable".equals(portState)) {
+ isEnabled = true;
+ } else if ("disable".equals(portState)) {
+ isEnabled = false;
+ } else {
+ print("State must be enable or disable");
+ return;
+ }
+
+ service.updatePortState(NetworkId.networkId(networkId),
+ DeviceId.deviceId(deviceId), vPort.number(), isEnabled);
+ print("Virtual port state updated.");
+ }
+
+ /**
+ * Returns the virtual port matching the device and port identifier.
+ *
+ * @param aPortNumber port identifier
+ * @return matching virtual port, or null.
+ */
+ private VirtualPort getVirtualPort(PortNumber aPortNumber) {
+ VirtualNetworkService service = get(VirtualNetworkService.class);
+ Set<VirtualPort> ports = service.getVirtualPorts(NetworkId.networkId(networkId),
+ DeviceId.deviceId(deviceId));
+ return ports.stream().filter(p -> p.number().equals(aPortNumber))
+ .findFirst().get();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/package-info.java
new file mode 100644
index 0000000..049ee4f
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * CLI commands for querying and administering virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.cli;
\ No newline at end of file