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/BUILD b/apps/virtual/BUILD
new file mode 100644
index 0000000..1f3cb57
--- /dev/null
+++ b/apps/virtual/BUILD
@@ -0,0 +1,13 @@
+BUNDLES = [
+ "//apps/virtual/api:onos-apps-virtual-api",
+ "//apps/virtual/app:onos-apps-virtual-app",
+]
+
+onos_app(
+ app_name = "org.onosproject.virtual",
+ category = "Traffic Engineering",
+ description = "Virtual Network Subsystem extension",
+ included_bundles = BUNDLES,
+ title = "Virtual Network Subsystem",
+ url = "http://onosproject.org",
+)
diff --git a/apps/virtual/api/BUILD b/apps/virtual/api/BUILD
new file mode 100644
index 0000000..da7ca40
--- /dev/null
+++ b/apps/virtual/api/BUILD
@@ -0,0 +1,11 @@
+COMPILE_DEPS = CORE_DEPS + JACKSON + [
+ "//apps/tunnel/api:onos-apps-tunnel-api",
+]
+
+TEST_DEPS = TEST_ADAPTERS
+
+osgi_jar_with_tests(
+ test_deps = TEST_DEPS,
+ visibility = ["//visibility:public"],
+ deps = COMPILE_DEPS,
+)
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/AbstractVnetService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/AbstractVnetService.java
new file mode 100644
index 0000000..b3e1d18
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/AbstractVnetService.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import org.onlab.osgi.ServiceDirectory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Basis for virtual network service.
+ */
+public abstract class AbstractVnetService
+ implements VnetService {
+
+ private static final String NETWORK_NULL = "Network ID cannot be null";
+
+ protected NetworkId networkId;
+ protected VirtualNetworkService manager;
+ protected ServiceDirectory serviceDirectory;
+
+ public AbstractVnetService(VirtualNetworkService manager,
+ NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ this.manager = manager;
+ this.networkId = networkId;
+ this.serviceDirectory = manager.getServiceDirectory();
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return this.networkId;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/Comparators.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/Comparators.java
new file mode 100644
index 0000000..8c67e00
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/Comparators.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-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;
+
+import java.util.Comparator;
+
+/**
+ * Various comparators.
+ */
+public final class Comparators {
+
+ // Ban construction
+ private Comparators() {
+ }
+
+ public static final Comparator<VirtualNetwork> VIRTUAL_NETWORK_COMPARATOR =
+ (v1, v2) -> {
+ int compareId = v1.tenantId().toString().compareTo(v2.tenantId().toString());
+ return (compareId != 0) ? compareId : Long.signum(v1.id().id() - v2.id().id());
+ };
+
+ public static final Comparator<VirtualDevice> VIRTUAL_DEVICE_COMPARATOR =
+ (v1, v2) -> v1.id().toString().compareTo(v2.id().toString());
+
+ public static final Comparator<VirtualPort> VIRTUAL_PORT_COMPARATOR =
+ (v1, v2) -> v1.number().toString().compareTo(v2.number().toString());
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
new file mode 100644
index 0000000..98e3e1e
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015-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;
+
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.*;
+
+/**
+ * Default representation of a virtual device.
+ */
+public final class DefaultVirtualDevice extends DefaultDevice implements VirtualDevice {
+
+ private static final String VIRTUAL = "virtual";
+ private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
+
+ private final NetworkId networkId;
+
+ /**
+ * Creates a network element attributed to the specified provider.
+ *
+ * @param networkId network identifier
+ * @param id device identifier
+ */
+ public DefaultVirtualDevice(NetworkId networkId, DeviceId id) {
+ super(PID, id, Type.VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL,
+ new ChassisId(0));
+ this.networkId = networkId;
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualDevice) {
+ DefaultVirtualDevice that = (DefaultVirtualDevice) obj;
+ return super.equals(that) && Objects.equals(this.networkId, that.networkId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("networkId", networkId).toString();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualHost.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualHost.java
new file mode 100644
index 0000000..61f31b1
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualHost.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default representation of a virtual host.
+ */
+public final class DefaultVirtualHost extends DefaultHost implements VirtualHost {
+
+ private static final String VIRTUAL = "virtual";
+ private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
+
+ private final NetworkId networkId;
+
+ /**
+ * Creates a virtual host attributed to the specified provider.
+ *
+ * @param networkId network identifier
+ * @param id host identifier
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param location host location
+ * @param ips host IP addresses
+ */
+ public DefaultVirtualHost(NetworkId networkId, HostId id, MacAddress mac,
+ VlanId vlan, HostLocation location, Set<IpAddress> ips) {
+ this(networkId, id, mac, vlan, Collections.singleton(location), ips);
+ }
+
+ /**
+ * Creates a virtual host attributed to the specified provider.
+ *
+ * @param networkId network identifier
+ * @param id host identifier
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param locations host locations
+ * @param ips host IP addresses
+ */
+ public DefaultVirtualHost(NetworkId networkId, HostId id, MacAddress mac,
+ VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips) {
+ super(PID, id, mac, vlan, locations, ips, false, DefaultAnnotations.builder().build());
+ this.networkId = networkId;
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualHost) {
+ DefaultVirtualHost that = (DefaultVirtualHost) obj;
+ return super.equals(that) && Objects.equals(this.networkId, that.networkId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("networkId", networkId).toString();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
new file mode 100644
index 0000000..20924bc
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default representation of a virtual link.
+ */
+public final class DefaultVirtualLink extends DefaultLink implements VirtualLink {
+
+ private static final String VIRTUAL = "virtualLink";
+ public static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
+
+ private final NetworkId networkId;
+ private final TunnelId tunnelId;
+
+ /**
+ * Private constructor for a default virtual link.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @param state link state
+ * @param tunnelId tunnel identifier
+ */
+ private DefaultVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst,
+ State state, TunnelId tunnelId) {
+ super(PID, src, dst, Type.VIRTUAL, state, DefaultAnnotations.builder().build());
+ this.networkId = networkId;
+ this.tunnelId = tunnelId;
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ /**
+ * Returns the tunnel identifier.
+ *
+ * @return tunnel identifier.
+ */
+ public TunnelId tunnelId() {
+ return tunnelId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, tunnelId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualLink) {
+ DefaultVirtualLink that = (DefaultVirtualLink) obj;
+ return super.equals(that) &&
+ Objects.equals(this.networkId, that.networkId) &&
+ Objects.equals(this.tunnelId, that.tunnelId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("networkId", networkId).add("tunnelId", tunnelId).toString();
+ }
+
+ /**
+ * Creates a new default virtual link builder.
+ *
+ * @return default virtual link builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for DefaultVirtualLink objects.
+ */
+ public static final class Builder extends DefaultLink.Builder {
+ private NetworkId networkId;
+ private ConnectPoint src;
+ private ConnectPoint dst;
+ private TunnelId tunnelId;
+ private State state;
+
+ private Builder() {
+ // Hide constructor
+ }
+
+ /**
+ * Sets the network identifier to be used by the builder.
+ *
+ * @param networkId network identifier
+ * @return self
+ */
+ public Builder networkId(NetworkId networkId) {
+ this.networkId = networkId;
+ return this;
+ }
+
+ /**
+ * Sets the source connect point to be used by the builder.
+ *
+ * @param src source connect point
+ * @return self
+ */
+ public Builder src(ConnectPoint src) {
+ this.src = src;
+ return this;
+ }
+
+ /**
+ * Sets the destination connect point to be used by the builder.
+ *
+ * @param dst new destination connect point
+ * @return self
+ */
+ public Builder dst(ConnectPoint dst) {
+ this.dst = dst;
+ return this;
+ }
+
+ /**
+ * Sets the tunnel identifier to be used by the builder.
+ *
+ * @param tunnelId tunnel identifier
+ * @return self
+ */
+ public Builder tunnelId(TunnelId tunnelId) {
+ this.tunnelId = tunnelId;
+ return this;
+ }
+
+ /**
+ * Sets the link state to be used by the builder.
+ *
+ * @param state link state
+ * @return self
+ */
+ public Builder state(State state) {
+ this.state = state;
+ return this;
+ }
+
+ /**
+ * Builds a default virtual link object from the accumulated parameters.
+ *
+ * @return default virtual link object
+ */
+ public DefaultVirtualLink build() {
+ checkNotNull(src, "Source connect point cannot be null");
+ checkNotNull(dst, "Destination connect point cannot be null");
+ checkNotNull(networkId, "Network Id cannot be null");
+
+ return new DefaultVirtualLink(networkId, src, dst, state, tunnelId);
+ }
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
new file mode 100644
index 0000000..bf7de57
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015-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;
+
+import org.onosproject.net.TenantId;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default implementation of the virtual network descriptor.
+ */
+public final class DefaultVirtualNetwork implements VirtualNetwork {
+
+ private final NetworkId id;
+ private final TenantId tenantId;
+
+ /**
+ * Creates a new virtual network descriptor.
+ *
+ * @param id network identifier
+ * @param tenantId tenant identifier
+ */
+ public DefaultVirtualNetwork(NetworkId id, TenantId tenantId) {
+ this.id = id;
+ this.tenantId = tenantId;
+ }
+
+ @Override
+ public NetworkId id() {
+ return id;
+ }
+
+ @Override
+ public TenantId tenantId() {
+ return tenantId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, tenantId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualNetwork) {
+ DefaultVirtualNetwork that = (DefaultVirtualNetwork) obj;
+ return Objects.equals(this.id, that.id)
+ && Objects.equals(this.tenantId, that.tenantId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("id", id)
+ .add("tenantId", tenantId)
+ .toString();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java
new file mode 100644
index 0000000..d5f3efb
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.Element;
+import org.onosproject.net.PortNumber;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default representation of a virtual port.
+ */
+public final class DefaultVirtualPort extends DefaultPort implements VirtualPort {
+
+ private final NetworkId networkId;
+ private final ConnectPoint realizedBy;
+
+ /**
+ * Creates a virtual port.
+ *
+ * @param networkId network identifier
+ * @param device parent network element
+ * @param portNumber port number
+ * @param realizedBy underling port which realizes this virtual port
+ */
+ public DefaultVirtualPort(NetworkId networkId, Device device, PortNumber portNumber,
+ ConnectPoint realizedBy) {
+ this(networkId, device, portNumber, false, realizedBy);
+ }
+
+ /**
+ * Creates a virtual port.
+ *
+ * @param networkId network identifier
+ * @param device parent network element
+ * @param portNumber port number
+ * @param isEnabled indicator whether the port is up and active
+ * @param realizedBy underling port which realizes this virtual port
+ */
+ public DefaultVirtualPort(NetworkId networkId, Device device, PortNumber portNumber,
+ boolean isEnabled, ConnectPoint realizedBy) {
+ super((Element) device, portNumber, isEnabled, DefaultAnnotations.builder().build());
+ this.networkId = networkId;
+ this.realizedBy = realizedBy;
+ }
+
+ /**
+ * Returns network identifier.
+ *
+ * @return network identifier
+ */
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ @Override
+ public ConnectPoint realizedBy() {
+ return realizedBy;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, realizedBy);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultVirtualPort) {
+ DefaultVirtualPort that = (DefaultVirtualPort) obj;
+ return super.equals(that) &&
+ Objects.equals(this.networkId, that.networkId) &&
+ Objects.equals(this.number(), that.number()) &&
+ Objects.equals(this.realizedBy, that.realizedBy);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("networkId", networkId).add("realizedBy", realizedBy).toString();
+ }
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/NetworkId.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/NetworkId.java
new file mode 100644
index 0000000..552e941
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/NetworkId.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.Identifier;
+
+import java.util.Objects;
+
+/**
+ * Representation of network identity.
+ */
+@Beta
+public final class NetworkId extends Identifier<Long> {
+
+ /**
+ * Represents no network, or an unspecified network.
+ */
+ public static final NetworkId NONE = networkId(-1L);
+
+ /**
+ * Represents the underlying physical network.
+ */
+ public static final NetworkId PHYSICAL = networkId(0L);
+
+ /**
+ * Checks if the id is for virtual network.
+ *
+ * @return true if the id is for virtual network.
+ */
+ public final boolean isVirtualNetworkId() {
+ return (!Objects.equals(this, NONE) && !Objects.equals(this, PHYSICAL));
+ }
+
+ // Public construction is prohibited
+ private NetworkId(long id) {
+ super(id);
+ }
+
+
+ // Default constructor for serialization
+ protected NetworkId() {
+ super(-1L);
+ }
+
+ /**
+ * Creates a network id using the supplied backing id.
+ *
+ * @param id network id
+ * @return network identifier
+ */
+ public static NetworkId networkId(long id) {
+ return new NetworkId(id);
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualDevice.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualDevice.java
new file mode 100644
index 0000000..d4b098f
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualDevice.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.Device;
+
+/**
+ * Abstraction of a virtual device.
+ */
+@Beta
+public interface VirtualDevice extends VirtualElement, Device {
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java
new file mode 100644
index 0000000..572d02d
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of a virtual element.
+ */
+@Beta
+public interface VirtualElement {
+
+ /**
+ * Returns the network identifier to which this virtual element belongs.
+ *
+ * @return network identifier
+ */
+ NetworkId networkId();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualHost.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualHost.java
new file mode 100644
index 0000000..569d8e3
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualHost.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.Host;
+
+/**
+ * Abstraction of a virtual end-station host.
+ */
+@Beta
+public interface VirtualHost extends VirtualElement, Host {
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualLink.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualLink.java
new file mode 100644
index 0000000..6f68961
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualLink.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.Link;
+
+/**
+ * Abstraction of a virtual link.
+ */
+@Beta
+public interface VirtualLink extends VirtualElement, Link {
+ /**
+ * Returns the tunnel identifier to which this virtual link belongs.
+ *
+ * @return tunnel identifier
+ */
+ TunnelId tunnelId();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetwork.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetwork.java
new file mode 100644
index 0000000..1cbbddb
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetwork.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.TenantId;
+
+/**
+ * Representation of a virtual network.
+ */
+@Beta
+public interface VirtualNetwork {
+
+ /**
+ * Returns the network identifier.
+ *
+ * @return network id
+ */
+ NetworkId id();
+
+ /**
+ * Returns the identifier of the tenant to which this virtual network belongs.
+ *
+ * @return tenant identifier
+ */
+ TenantId tenantId();
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java
new file mode 100644
index 0000000..bade4078
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TenantId;
+
+import java.util.Set;
+
+/**
+ * Service for managing the inventory of virtual networks.
+ */
+@Beta
+public interface VirtualNetworkAdminService extends VirtualNetworkService {
+
+ /**
+ * Registers the specified, externally generated tenant identifier.
+ *
+ * @param tenantId tenant identifier
+ */
+ void registerTenantId(TenantId tenantId);
+
+ /**
+ * Unregisters the specified, externally generated tenant identifier.
+ *
+ * @param tenantId tenant identifier
+ * @throws IllegalStateException if there are networks still owned by this tenant
+ */
+ void unregisterTenantId(TenantId tenantId);
+
+ /**
+ * Returns the set of tenant identifiers known to the system.
+ *
+ * @return set of known tenant identifiers
+ */
+ Set<TenantId> getTenantIds();
+
+ /**
+ * Creates a new virtual network for the specified tenant.
+ *
+ * @param tenantId tenant identifier
+ * @return newly created virtual network
+ */
+ VirtualNetwork createVirtualNetwork(TenantId tenantId);
+
+ /**
+ * Removes the specified virtual network and all its devices and links.
+ *
+ * @param networkId network identifier
+ */
+ void removeVirtualNetwork(NetworkId networkId);
+
+ /**
+ * Creates a new virtual device within the specified network. The device id
+ * must be unique within the bounds of the network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @return newly created virtual device
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Removes the specified virtual device and all its ports and affiliated links.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @throws org.onlab.util.ItemNotFoundException if no such network or device found
+ */
+ void removeVirtualDevice(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Creates a new virtual host within the specified network. The host id
+ * must be unique within the bounds of the network.
+ *
+ * @param networkId network identifier
+ * @param hostId host identifier
+ * @param mac mac address
+ * @param vlan vlan identifier
+ * @param location host location
+ * @param ips set of ip addresses
+ * @return newly created virtual host
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ VirtualHost createVirtualHost(NetworkId networkId, HostId hostId, MacAddress mac,
+ VlanId vlan, HostLocation location, Set<IpAddress> ips);
+
+ /**
+ * Removes the specified virtual host.
+ *
+ * @param networkId network identifier
+ * @param hostId host identifier
+ * @throws org.onlab.util.ItemNotFoundException if no such network or host found
+ */
+ void removeVirtualHost(NetworkId networkId, HostId hostId);
+
+ /**
+ * Creates a new virtual link within the specified network.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @return newly created virtual link
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ VirtualLink createVirtualLink(NetworkId networkId,
+ ConnectPoint src, ConnectPoint dst);
+
+ // TODO: Discuss whether we should provide an alternate createVirtualLink
+ // which is backed by a Path instead; I'm leaning towards not doing that.
+
+ /**
+ * Removes the specified virtual link.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @throws org.onlab.util.ItemNotFoundException if no such network or link found
+ */
+ void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+ /**
+ * Creates a new virtual port on the specified device.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device identifier
+ * @param portNumber virtual port number
+ * @param realizedBy underlying physical port using which this virtual port is realized
+ * @return newly created port
+ * @throws org.onlab.util.ItemNotFoundException if no such network or device is found
+ */
+ VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy);
+
+ /**
+ * Binds an existing virtual port on the specified device.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device identifier
+ * @param portNumber virtual port number
+ * @param realizedBy underlying physical port using which this virtual port is realized
+ * @throws org.onlab.util.ItemNotFoundException if no such network or device is found
+ */
+ void bindVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy);
+
+ /**
+ * Updates port state of an existing virtual port on the specified device.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device identifier
+ * @param portNumber virtual port number
+ * @param isEnabled indicator whether the port is up and active
+ * @throws org.onlab.util.ItemNotFoundException if no such network or device is found
+ */
+ void updatePortState(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, boolean isEnabled);
+
+ /**
+ * Removes the specified virtual port.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @param portNumber port number
+ * @throws org.onlab.util.ItemNotFoundException if no such network or port found
+ */
+ void removeVirtualPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
new file mode 100644
index 0000000..3f96496
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2015-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;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes virtual network event.
+ */
+public class VirtualNetworkEvent extends AbstractEvent<VirtualNetworkEvent.Type, NetworkId> {
+
+ /**
+ * Type of virtual network events.
+ */
+ public enum Type {
+ /**
+ * Signifies that a new tenant identifier was registered.
+ */
+ TENANT_REGISTERED,
+ /**
+ * Signifies that a tenant identifier was unregistered.
+ */
+ TENANT_UNREGISTERED,
+ /**
+ * Signifies that a new virtual network was added.
+ */
+ NETWORK_ADDED,
+ /**
+ * Signifies that a virtual network was updated.
+ */
+ NETWORK_UPDATED,
+ /**
+ * Signifies that a virtual network was removed.
+ */
+ NETWORK_REMOVED,
+ /**
+ * Signifies that a new virtual network device was added.
+ */
+ VIRTUAL_DEVICE_ADDED,
+ /**
+ * Signifies that a virtual network device was updated.
+ */
+ VIRTUAL_DEVICE_UPDATED,
+ /**
+ * Signifies that a virtual network device was removed.
+ */
+ VIRTUAL_DEVICE_REMOVED,
+ /**
+ * Signifies that a new virtual network port was added.
+ */
+ VIRTUAL_PORT_ADDED,
+ /**
+ * Signifies that a virtual network port was updated.
+ */
+ VIRTUAL_PORT_UPDATED,
+ /**
+ * Signifies that a virtual network port was removed.
+ */
+ VIRTUAL_PORT_REMOVED
+ }
+
+ private final VirtualDevice virtualDevice;
+ private final VirtualPort virtualPort;
+
+ /**
+ * Creates an event of a given type and for the specified subject.
+ *
+ * @param type event type
+ * @param subject event subject
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject) {
+ this(type, subject, null, null);
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject and the
+ * virtual device.
+ *
+ * @param type event type
+ * @param subject event subject
+ * @param virtualDevice virtual device
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice) {
+ this(type, subject, virtualDevice, null);
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject, virtual device and
+ * virtual port.
+ *
+ * @param type event type
+ * @param subject event subject
+ * @param virtualDevice virtual device
+ * @param virtualPort virtual port
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+ VirtualPort virtualPort) {
+ super(type, subject);
+ this.virtualDevice = virtualDevice;
+ this.virtualPort = virtualPort;
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject and time.
+ *
+ * @param type event type
+ * @param subject event subject
+ * @param time occurrence time
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject, long time) {
+ this(type, subject, null, null, time);
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject, virtual device and time.
+ *
+ * @param type event type
+ * @param subject event subject
+ * @param virtualDevice virtual device
+ * @param time occurrence time
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+ long time) {
+ this(type, subject, virtualDevice, null, time);
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject, virtual port and time.
+ *
+ * @param type device event type
+ * @param subject event subject
+ * @param virtualPort virtual port
+ * @param time occurrence time
+ */
+ public VirtualNetworkEvent(Type type, NetworkId subject, VirtualPort virtualPort,
+ long time) {
+ this(type, subject, null, virtualPort, time);
+ }
+
+ /**
+ * Creates an event of a given type and for the specified subject, virtual device,
+ * virtual port and time.
+ *
+ * @param type device event type
+ * @param subject event subject
+ * @param virtualDevice virtual device
+ * @param virtualPort virtual port
+ * @param time occurrence time
+ */
+ private VirtualNetworkEvent(Type type, NetworkId subject, VirtualDevice virtualDevice,
+ VirtualPort virtualPort, long time) {
+ super(type, subject, time);
+ this.virtualDevice = virtualDevice;
+ this.virtualPort = virtualPort;
+ }
+
+ /**
+ * Returns virtual device affected by event - may be null (for events relating to
+ * tenants and virtual networks).
+ *
+ * @return virtual device
+ */
+ public VirtualDevice virtualDevice() {
+ return virtualDevice;
+ }
+
+ /**
+ * Returns virtual port affected by event - may be null (for events relating to
+ * tenants, virtual networks and virtual devices).
+ *
+ * @return virtual port
+ */
+ public VirtualPort virtualPort() {
+ return virtualPort;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowObjectiveStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowObjectiveStore.java
new file mode 100644
index 0000000..c118958
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowObjectiveStore.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+
+import java.util.Map;
+
+/**
+ * The flow objective store for virtual networks.
+ */
+public interface VirtualNetworkFlowObjectiveStore
+ extends VirtualStore<ObjectiveEvent, FlowObjectiveStoreDelegate> {
+
+ /**
+ * Adds a NextGroup to the store, by mapping it to the nextId as key,
+ * and replacing any previous mapping.
+ *
+ * @param networkId a virtual network identifier
+ * @param nextId an integer
+ * @param group a next group opaque object
+ */
+ void putNextGroup(NetworkId networkId, Integer nextId, NextGroup group);
+
+ /**
+ * Fetch a next group from the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param nextId an integer used as key
+ * @return a next group, or null if group was not found
+ */
+ NextGroup getNextGroup(NetworkId networkId, Integer nextId);
+
+ /**
+ * Remove a next group mapping from the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param nextId the key to remove from the store.
+ * @return the next group which mapped to the nextId and is now removed, or
+ * null if no group mapping existed in the store
+ */
+ NextGroup removeNextGroup(NetworkId networkId, Integer nextId);
+
+ /**
+ * Fetch all groups from the store and their mapping to nextIds.
+ *
+ * @param networkId a virtual network identifier
+ * @return a map that represents the current snapshot of Next-ids to NextGroups
+ */
+ Map<Integer, NextGroup> getAllGroups(NetworkId networkId);
+
+ /**
+ * Allocates a next objective id. This id is globally unique.
+ *
+ * @param networkId a virtual network identifier
+ * @return an integer
+ */
+ int allocateNextId(NetworkId networkId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowRuleStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowRuleStore.java
new file mode 100644
index 0000000..6b532c3
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkFlowRuleStore.java
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.flow.TableStatisticsEntry;
+
+import java.util.List;
+
+/**
+ * Manages inventory of flow rules for virtual networks;
+ * not intended for direct use.
+ */
+public interface VirtualNetworkFlowRuleStore
+ extends VirtualStore<FlowRuleBatchEvent, FlowRuleStoreDelegate> {
+ /**
+ * Returns the number of flow rule in the store.
+ *
+ * @param networkId virtual network identifier
+ * @return number of flow rules
+ */
+ int getFlowRuleCount(NetworkId networkId);
+
+ /**
+ * Returns the stored flow.
+ *
+ * @param networkId virtual network identifier
+ * @param rule the rule to look for
+ * @return a flow rule
+ */
+ FlowEntry getFlowEntry(NetworkId networkId, FlowRule rule);
+
+ /**
+ * Returns the flow entries associated with a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device ID
+ * @return the flow entries
+ */
+ Iterable<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Stores a batch of flow rules.
+ *
+ * @param networkId virtual network identifier
+ * @param batchOperation batch of flow rules.
+ * A batch can contain flow rules for a single device only.
+ *
+ */
+ void storeBatch(NetworkId networkId, FlowRuleBatchOperation batchOperation);
+
+ /**
+ * Invoked on the completion of a storeBatch operation.
+ *
+ * @param networkId virtual network identifier
+ * @param event flow rule batch event
+ */
+ void batchOperationComplete(NetworkId networkId, FlowRuleBatchEvent event);
+
+ /**
+ * Marks a flow rule for deletion. Actual deletion will occur
+ * when the provider indicates that the flow has been removed.
+ *
+ * @param networkId virtual network identifier
+ * @param rule the flow rule to delete
+ */
+ void deleteFlowRule(NetworkId networkId, FlowRule rule);
+
+ /**
+ * Stores a new flow rule, or updates an existing entry.
+ *
+ * @param networkId virtual network identifier
+ * @param rule the flow rule to add or update
+ * @return flow_added event, or null if just an update
+ */
+ FlowRuleEvent addOrUpdateFlowRule(NetworkId networkId, FlowEntry rule);
+
+ /**
+ * Removes an existing flow entry.
+ *
+ * @param rule the flow entry to remove
+ * @param networkId virtual network identifier
+ * @return flow_removed event, or null if nothing removed
+ */
+ FlowRuleEvent removeFlowRule(NetworkId networkId, FlowEntry rule);
+
+ /**
+ * Marks a flow rule as PENDING_ADD during retry.
+ *
+ * Emits flow_update event if the state is changed
+ *
+ * @param networkId virtual network identifier
+ * @param rule the flow rule that is retrying
+ * @return flow_updated event, or null if nothing updated
+ */
+ FlowRuleEvent pendingFlowRule(NetworkId networkId, FlowEntry rule);
+
+ /**
+ * Removes all flow entries of given device from store.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId device id
+ */
+ default void purgeFlowRule(NetworkId networkId, DeviceId deviceId) {}
+
+ /**
+ * Removes all flow entries from store.
+ *
+ * @param networkId virtual network identifier
+ */
+ void purgeFlowRules(NetworkId networkId);
+
+ /**
+ * Updates the flow table statistics of the specified device using
+ * the given statistics.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId device identifier
+ * @param tableStats list of table statistics
+ * @return ready to send event describing what occurred;
+ */
+ FlowRuleEvent updateTableStatistics(NetworkId networkId, DeviceId deviceId,
+ List<TableStatisticsEntry> tableStats);
+
+ /**
+ * Returns the flow table statistics associated with a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device ID
+ * @return the flow table statistics
+ */
+ Iterable<TableStatisticsEntry> getTableStatistics(NetworkId networkId, DeviceId deviceId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkGroupStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkGroupStore.java
new file mode 100644
index 0000000..d080524
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkGroupStore.java
@@ -0,0 +1,231 @@
+/*
+ * 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;
+
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupStoreDelegate;
+
+import java.util.Collection;
+
+/**
+ * Manages inventory of groups per virtual network and virtual device;
+ * not intended for direct use.
+ */
+public interface VirtualNetworkGroupStore
+ extends VirtualStore<GroupEvent, GroupStoreDelegate> {
+
+ enum UpdateType {
+ /**
+ * Modify existing group entry by adding provided information.
+ */
+ ADD,
+ /**
+ * Modify existing group by removing provided information from it.
+ */
+ REMOVE,
+ /**
+ * Modify existing group entry by setting the provided information,
+ * overwriting the previous group entry entirely.
+ */
+ SET
+ }
+
+ /**
+ * Returns the number of groups for the specified virtual device in the store.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @return number of groups for the specified device
+ */
+ int getGroupCount(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the groups associated with a virtual device.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @return the group entries
+ */
+ Iterable<Group> getGroups(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the stored group entry in a virtual network.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ * @return a group associated with the key
+ */
+ Group getGroup(NetworkId networkId, DeviceId deviceId, GroupKey appCookie);
+
+ /**
+ * Returns the stored group entry for an id.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param groupId the group identifier
+ * @return a group associated with the key
+ */
+ Group getGroup(NetworkId networkId, DeviceId deviceId, GroupId groupId);
+
+ /**
+ * Stores a new group entry using the information from group description
+ * for a virtual network.
+ *
+ * @param networkId the virtual network ID
+ * @param groupDesc group description to be used to store group entry
+ */
+ void storeGroupDescription(NetworkId networkId, GroupDescription groupDesc);
+
+ /**
+ * Updates the existing group entry with the information
+ * from group description.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param oldAppCookie the current group key
+ * @param type update type
+ * @param newBuckets group buckets for updates
+ * @param newAppCookie optional new group key
+ */
+ void updateGroupDescription(NetworkId networkId,
+ DeviceId deviceId,
+ GroupKey oldAppCookie,
+ UpdateType type,
+ GroupBuckets newBuckets,
+ GroupKey newAppCookie);
+
+ /**
+ * Triggers deleting the existing group entry.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param appCookie the group key
+ */
+ void deleteGroupDescription(NetworkId networkId,
+ DeviceId deviceId,
+ GroupKey appCookie);
+
+ /**
+ * Stores a new group entry, or updates an existing entry.
+ *
+ * @param networkId the virtual network ID
+ * @param group group entry
+ */
+ void addOrUpdateGroupEntry(NetworkId networkId, Group group);
+
+ /**
+ * Removes the group entry from store.
+ *
+ * @param networkId the virtual network ID
+ * @param group group entry
+ */
+ void removeGroupEntry(NetworkId networkId, Group group);
+
+ /**
+ * Removes all group entries of given device from store.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId device id
+ */
+ void purgeGroupEntry(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Removes all group entries from store.
+ *
+ * @param networkId the virtual network ID
+ */
+ default void purgeGroupEntries(NetworkId networkId) {}
+
+ /**
+ * A group entry that is present in switch but not in the store.
+ *
+ * @param networkId the virtual network ID
+ * @param group group entry
+ */
+ void addOrUpdateExtraneousGroupEntry(NetworkId networkId, Group group);
+
+ /**
+ * Remove the group entry from extraneous database.
+ *
+ * @param networkId the virtual network ID
+ * @param group group entry
+ */
+ void removeExtraneousGroupEntry(NetworkId networkId, Group group);
+
+ /**
+ * Returns the extraneous groups associated with a device.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ *
+ * @return the extraneous group entries
+ */
+ Iterable<Group> getExtraneousGroups(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Indicates the first group audit is completed.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param completed initial audit status
+ */
+ void deviceInitialAuditCompleted(NetworkId networkId, DeviceId deviceId, boolean completed);
+
+ /**
+ * Retrieves the initial group audit status for a device.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ *
+ * @return initial group audit status
+ */
+ boolean deviceInitialAuditStatus(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Indicates the group operations failed.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param operation the group operation failed
+ */
+ void groupOperationFailed(NetworkId networkId, DeviceId deviceId, GroupOperation operation);
+
+ /**
+ * Submits the group metrics to store for a given device ID.
+ *
+ * @param networkId the virtual network ID
+ * @param deviceId the device ID
+ * @param groupEntries the group entries as received from southbound
+ */
+ void pushGroupMetrics(NetworkId networkId, DeviceId deviceId, Collection<Group> groupEntries);
+
+ /**
+ * Indicates failover within a failover group.
+ *
+ * @param networkId the virtual network ID
+ * @param failoverGroups groups to notify
+ */
+ void notifyOfFailovers(NetworkId networkId, Collection<Group> failoverGroups);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntent.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntent.java
new file mode 100644
index 0000000..817db21
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntent.java
@@ -0,0 +1,260 @@
+/*
+ * 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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstraction of VirtualNetworkIntent connectivity.
+ */
+@Beta
+public final class VirtualNetworkIntent extends ConnectivityIntent {
+
+ private final NetworkId networkId;
+ private final ConnectPoint ingressPoint;
+ private final ConnectPoint egressPoint;
+
+ private static final String NETWORK_ID_NULL = "Network ID cannot be null";
+
+ /**
+ * Returns a new point to point intent builder. The application id,
+ * ingress point and egress point are required fields. If they are
+ * not set by calls to the appropriate methods, an exception will
+ * be thrown.
+ *
+ * @return point to point builder
+ */
+ public static VirtualNetworkIntent.Builder builder() {
+ return new VirtualNetworkIntent.Builder();
+ }
+
+ /**
+ * Builder of a point to point intent.
+ */
+ public static final class Builder extends ConnectivityIntent.Builder {
+ NetworkId networkId;
+ ConnectPoint ingressPoint;
+ ConnectPoint egressPoint;
+
+ /**
+ * Builder constructor.
+ */
+ private Builder() {
+ // Hide constructor
+ }
+
+ @Override
+ public Builder appId(ApplicationId appId) {
+ return (Builder) super.appId(appId);
+ }
+
+ @Override
+ public Builder key(Key key) {
+ return (Builder) super.key(key);
+ }
+
+ @Override
+ public Builder selector(TrafficSelector selector) {
+ return (Builder) super.selector(selector);
+ }
+
+ @Override
+ public Builder treatment(TrafficTreatment treatment) {
+ return (Builder) super.treatment(treatment);
+ }
+
+ @Override
+ public Builder constraints(List<Constraint> constraints) {
+ return (Builder) super.constraints(constraints);
+ }
+
+ @Override
+ public Builder priority(int priority) {
+ return (Builder) super.priority(priority);
+ }
+
+ @Override
+ public Builder resourceGroup(ResourceGroup resourceGroup) {
+ return (Builder) super.resourceGroup(resourceGroup);
+ }
+
+ /**
+ * Sets the virtual network of the virtual network intent.
+ *
+ * @param networkId virtual network identifier
+ * @return this builder
+ */
+ public VirtualNetworkIntent.Builder networkId(NetworkId networkId) {
+ this.networkId = networkId;
+ return this;
+ }
+
+ /**
+ * Sets the ingress point of the virtual network intent that will be built.
+ *
+ * @param ingressPoint ingress connect point
+ * @return this builder
+ */
+ public VirtualNetworkIntent.Builder ingressPoint(ConnectPoint ingressPoint) {
+ this.ingressPoint = ingressPoint;
+ return this;
+ }
+
+ /**
+ * Sets the egress point of the virtual network intent that will be built.
+ *
+ * @param egressPoint egress connect point
+ * @return this builder
+ */
+ public VirtualNetworkIntent.Builder egressPoint(ConnectPoint egressPoint) {
+ this.egressPoint = egressPoint;
+ return this;
+ }
+
+ /**
+ * Builds a virtual network intent from the accumulated parameters.
+ *
+ * @return virtual network intent
+ */
+ public VirtualNetworkIntent build() {
+
+ return new VirtualNetworkIntent(
+ networkId,
+ appId,
+ key,
+ selector,
+ treatment,
+ ingressPoint,
+ egressPoint,
+ constraints,
+ priority,
+ resourceGroup
+ );
+ }
+ }
+
+
+ /**
+ * Creates a new point-to-point intent with the supplied ingress/egress
+ * ports and constraints.
+ *
+ * @param networkId virtual network identifier
+ * @param appId application identifier
+ * @param key key of the intent
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param ingressPoint ingress port
+ * @param egressPoint egress port
+ * @param constraints optional list of constraints
+ * @param priority priority to use for flows generated by this intent
+ * @throws NullPointerException if {@code ingressPoint} or
+ * {@code egressPoints} or {@code appId} is null.
+ */
+ private VirtualNetworkIntent(NetworkId networkId,
+ ApplicationId appId,
+ Key key,
+ TrafficSelector selector,
+ TrafficTreatment treatment,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint,
+ List<Constraint> constraints,
+ int priority,
+ ResourceGroup resourceGroup) {
+ super(appId, key, Collections.emptyList(), selector, treatment, constraints,
+ priority, resourceGroup);
+
+ checkNotNull(networkId, NETWORK_ID_NULL);
+ checkArgument(!ingressPoint.equals(egressPoint),
+ "ingress and egress should be different (ingress: %s, egress: %s)", ingressPoint, egressPoint);
+
+ this.networkId = networkId;
+ this.ingressPoint = checkNotNull(ingressPoint);
+ this.egressPoint = checkNotNull(egressPoint);
+ }
+
+ /**
+ * Constructor for serializer.
+ */
+ protected VirtualNetworkIntent() {
+ super();
+ this.networkId = null;
+ this.ingressPoint = null;
+ this.egressPoint = null;
+ }
+
+ /**
+ * Returns the virtual network identifier.
+ *
+ * @return network identifier
+ */
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ /**
+ * Returns the port on which the ingress traffic should be connected to
+ * the egress.
+ *
+ * @return ingress port
+ */
+ public ConnectPoint ingressPoint() {
+ return ingressPoint;
+ }
+
+ /**
+ * Returns the port on which the traffic should egress.
+ *
+ * @return egress port
+ */
+ public ConnectPoint egressPoint() {
+ return egressPoint;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("networkId", networkId)
+ .add("id", id())
+ .add("key", key())
+ .add("appId", appId())
+ .add("priority", priority())
+ .add("resources", resources())
+ .add("selector", selector())
+ .add("treatment", treatment())
+ .add("ingress", ingressPoint)
+ .add("egress", egressPoint)
+ .add("constraints", constraints())
+ .add("resourceGroup", resourceGroup())
+ .toString();
+ }
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java
new file mode 100644
index 0000000..f8b42c1
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+
+public interface VirtualNetworkIntentStore
+ extends VirtualStore<IntentEvent, IntentStoreDelegate> {
+
+ /**
+ * Returns the number of intents in the store.
+ *
+ * @param networkId the virtual network identifier
+ * @return the number of intents in the store
+ */
+ long getIntentCount(NetworkId networkId);
+
+ /**
+ * Returns an iterable of all intents in the store.
+ *
+ * @param networkId the virtual network identifier
+ * @return iterable of all intents
+ */
+ Iterable<Intent> getIntents(NetworkId networkId);
+
+ /**
+ * Returns an iterable of all intent data objects in the store.
+ *
+ * @param networkId the virtual network identifier
+ * @param localOnly should only intents for which this instance is master
+ * be returned
+ * @param olderThan specified duration in milliseconds (0 for "now")
+ * @return iterable of all intent data objects
+ */
+ Iterable<IntentData> getIntentData(NetworkId networkId, boolean localOnly,
+ long olderThan);
+
+ /**
+ * Returns the state of the specified intent.
+ *
+ * @param networkId the virtual network identifier
+ * @param intentKey intent identification
+ * @return current intent state
+ */
+ IntentState getIntentState(NetworkId networkId, Key intentKey);
+
+ /**
+ * Returns the list of the installable events associated with the specified
+ * original intent.
+ *
+ * @param networkId the virtual network identifier
+ * @param intentKey original intent identifier
+ * @return compiled installable intents, or null if no installables exist
+ */
+ List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey);
+
+ /**
+ * Writes an IntentData object to the store.
+ *
+ * @param networkId the virtual network identifier
+ * @param newData new intent data to write
+ */
+ void write(NetworkId networkId, IntentData newData);
+
+ /**
+ * Writes a batch of IntentData objects to the store. A batch has no
+ * semantics, this is simply a convenience API.
+ *
+ * @param networkId the virtual network identifier
+ * @param updates collection of intent data objects to write
+ */
+ void batchWrite(NetworkId networkId, Iterable<IntentData> updates);
+
+ /**
+ * Returns the intent with the specified identifier.
+ *
+ * @param networkId the virtual network identifier
+ * @param key key
+ * @return intent or null if not found
+ */
+ Intent getIntent(NetworkId networkId, Key key);
+
+ /**
+ * Returns the intent data object associated with the specified key.
+ *
+ * @param networkId the virtual network identifier
+ * @param key key to look up
+ * @return intent data object
+ */
+ IntentData getIntentData(NetworkId networkId, Key key);
+
+ /**
+ * Adds a new operation, which should be persisted and delegated.
+ *
+ * @param networkId the virtual network identifier
+ * @param intent operation
+ */
+ void addPending(NetworkId networkId, IntentData intent);
+
+ /**
+ * Checks to see whether the calling instance is the master for processing
+ * this intent, or more specifically, the key contained in this intent.
+ *
+ * @param networkId the virtual network identifier
+ * @param intentKey intentKey to check
+ * @return true if master; false, otherwise
+ */
+ //TODO better name
+ boolean isMaster(NetworkId networkId, Key intentKey);
+
+ /**
+ * Returns the intent requests pending processing.
+ *
+ * @param networkId the virtual network identifier
+ * @return pending intents
+ */
+ Iterable<Intent> getPending(NetworkId networkId);
+
+ /**
+ * Returns the intent data objects that are pending processing.
+ *
+ * @param networkId the virtual network identifier
+ * @return pending intent data objects
+ */
+ Iterable<IntentData> getPendingData(NetworkId networkId);
+
+ /**
+ * Returns the intent data object that are pending processing for a specfied
+ * key.
+ *
+ * @param networkId the virtual network identifier
+ * @param intentKey key to look up
+ * @return pending intent data object
+ */
+ IntentData getPendingData(NetworkId networkId, Key intentKey);
+
+ /**
+ * Returns the intent data objects that are pending processing for longer
+ * than the specified duration.
+ *
+ * @param networkId the virtual network identifier
+ * @param localOnly should only intents for which this instance is master
+ * be returned
+ * @param olderThan specified duration in milliseconds (0 for "now")
+ * @return pending intent data objects
+ */
+ Iterable<IntentData> getPendingData(NetworkId networkId, boolean localOnly, long olderThan);
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java
new file mode 100644
index 0000000..cc6d663
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015-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;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Represents entity capable of receiving virtual network events.
+ */
+public interface VirtualNetworkListener extends EventListener<VirtualNetworkEvent> {
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMastershipStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMastershipStore.java
new file mode 100644
index 0000000..31fdfcd
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMastershipStore.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipInfo;
+import org.onosproject.mastership.MastershipStoreDelegate;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Manages inventory of mastership roles for devices, across controller
+ * instances for virtual networks; not intended for direct use.
+ */
+public interface VirtualNetworkMastershipStore
+ extends VirtualStore<MastershipEvent, MastershipStoreDelegate> {
+
+ // three things to map: NodeId, DeviceId, MastershipRole
+
+ /**
+ * Requests role of the local node for the specified device.
+ *
+ * @param networkId the virtual network identifier
+ * @param deviceId the device identifier
+ * @return established or newly negotiated mastership role
+ */
+ CompletableFuture<MastershipRole> requestRole(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the role of a device for a specific controller instance.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId the instance identifier
+ * @param deviceId the device identifiers
+ * @return the role
+ */
+ MastershipRole getRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId);
+
+ /**
+ * Returns the master for a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device identifier
+ * @return the instance identifier of the master
+ */
+ NodeId getMaster(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the master and backup nodes for a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device identifier
+ * @return a RoleInfo containing controller IDs
+ */
+ RoleInfo getNodes(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the mastership info for a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device identifier
+ * @return the mastership info
+ */
+ MastershipInfo getMastership(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns the devices that a controller instance is master of.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId the instance identifier
+ * @return a set of device identifiers
+ */
+ Set<DeviceId> getDevices(NetworkId networkId, NodeId nodeId);
+
+
+ /**
+ * Sets a device's role for a specified controller instance.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId controller instance identifier
+ * @param deviceId device identifier
+ * @return a mastership event
+ */
+ CompletableFuture<MastershipEvent> setMaster(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId);
+
+ /**
+ * Returns the current master and number of past mastership hand-offs
+ * (terms) for a device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId the device identifier
+ * @return the current master's ID and the term value for device, or null
+ */
+ MastershipTerm getTermFor(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Sets a controller instance's mastership role to STANDBY for a device.
+ * If the role is MASTER, another controller instance will be selected
+ * as a candidate master.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId the controller instance identifier
+ * @param deviceId device to revoke mastership role for
+ * @return a mastership event
+ */
+ CompletableFuture<MastershipEvent> setStandby(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId);
+
+ /**
+ * Allows a controller instance to give up its current role for a device.
+ * If the role is MASTER, another controller instance will be selected
+ * as a candidate master.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId the controller instance identifier
+ * @param deviceId device to revoke mastership role for
+ * @return a mastership event
+ */
+ CompletableFuture<MastershipEvent> relinquishRole(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId);
+
+ /**
+ * Removes all the roles for the specified controller instance.
+ * If the role was MASTER, another controller instance will be selected
+ * as a candidate master.
+ *
+ * @param networkId virtual network identifier
+ * @param nodeId the controller instance identifier
+ */
+ void relinquishAllRole(NetworkId networkId, NodeId nodeId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMeterStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMeterStore.java
new file mode 100644
index 0000000..954f66b
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkMeterStore.java
@@ -0,0 +1,135 @@
+/*
+ * 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;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterEvent;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterFeatures;
+import org.onosproject.net.meter.MeterFeaturesKey;
+import org.onosproject.net.meter.MeterKey;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterStoreDelegate;
+import org.onosproject.net.meter.MeterStoreResult;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+public interface VirtualNetworkMeterStore
+ extends VirtualStore<MeterEvent, MeterStoreDelegate> {
+
+ /**
+ * Adds a meter to the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param meter a meter
+ * @return a future indicating the result of the store operation
+ */
+ CompletableFuture<MeterStoreResult> storeMeter(NetworkId networkId, Meter meter);
+
+ /**
+ * Deletes a meter from the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param meter a meter
+ * @return a future indicating the result of the store operation
+ */
+ CompletableFuture<MeterStoreResult> deleteMeter(NetworkId networkId, Meter meter);
+
+
+ /**
+ * Adds the meter features to the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param meterfeatures the meter features
+ * @return the result of the store operation
+ */
+ MeterStoreResult storeMeterFeatures(NetworkId networkId, MeterFeatures meterfeatures);
+
+ /**
+ * Deletes the meter features from the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId the device id
+ * @return a future indicating the result of the store operation
+ */
+ MeterStoreResult deleteMeterFeatures(NetworkId networkId, DeviceId deviceId);
+
+
+ /**
+ * Updates a meter whose meter id is the same as the passed meter.
+ *
+ * @param networkId a virtual network identifier
+ * @param meter a new meter
+ * @return a future indicating the result of the store operation
+ */
+ CompletableFuture<MeterStoreResult> updateMeter(NetworkId networkId, Meter meter);
+
+ /**
+ * Updates a given meter's state with the provided state.
+ *
+ * @param networkId a virtual network identifier
+ * @param meter a meter
+ */
+ void updateMeterState(NetworkId networkId, Meter meter);
+
+ /**
+ * Obtains a meter matching the given meter key.
+ *
+ * @param networkId a virtual network identifier
+ * @param key a meter key
+ * @return a meter
+ */
+ Meter getMeter(NetworkId networkId, MeterKey key);
+
+ /**
+ * Returns all meters stored in the store.
+ *
+ * @param networkId a virtual network identifier
+ * @return a collection of meters
+ */
+ Collection<Meter> getAllMeters(NetworkId networkId);
+
+ /**
+ * Update the store by deleting the failed meter.
+ * Notifies the delegate that the meter failed to allow it
+ * to nofity the app.
+ *
+ * @param networkId a virtual network identifier
+ * @param op a failed meter operation
+ * @param reason a failure reason
+ */
+ void failedMeter(NetworkId networkId, MeterOperation op, MeterFailReason reason);
+
+ /**
+ * Delete this meter immediately.
+ *
+ * @param networkId a virtual network identifier
+ * @param m a meter
+ */
+ void deleteMeterNow(NetworkId networkId, Meter m);
+
+ /**
+ * Retrieve maximum meters available for the device.
+ *
+ * @param networkId a virtual network identifier
+ * @param key the meter features key
+ * @return the maximum number of meters supported by the device
+ */
+ long getMaxMeters(NetworkId networkId, MeterFeaturesKey key);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkPacketStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkPacketStore.java
new file mode 100644
index 0000000..4cf5cb9
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkPacketStore.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketStoreDelegate;
+
+import java.util.List;
+
+public interface VirtualNetworkPacketStore
+ extends VirtualStore<PacketEvent, PacketStoreDelegate> {
+ /**
+ * Decides which instance should emit the packet and forwards the packet to
+ * that instance. The relevant PacketManager is notified via the
+ * PacketStoreDelegate that it should emit the packet.
+ *
+ * @param networkId a virtual network identifier
+ * @param packet the packet to emit
+ */
+ void emit(NetworkId networkId, OutboundPacket packet);
+
+ /**
+ * Requests intercept of packets that match the given selector.
+ *
+ * @param networkId a virtual network identifier
+ * @param request a packet request
+ */
+ void requestPackets(NetworkId networkId, PacketRequest request);
+
+ /**
+ * Cancels intercept of packets that match the given selector.
+ *
+ * @param networkId a virtual network identifier
+ * @param request a packet request
+ */
+ void cancelPackets(NetworkId networkId, PacketRequest request);
+
+ /**
+ * Obtains all existing requests in the system.
+ *
+ * @param networkId a virtual network identifier
+ * @return list of packet requests in order of priority
+ */
+ List<PacketRequest> existingRequests(NetworkId networkId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
new file mode 100644
index 0000000..d0e4fd1
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.event.ListenerService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.TenantId;
+
+import java.util.Set;
+
+/**
+ * Service for querying virtual network inventory.
+ */
+@Beta
+public interface VirtualNetworkService
+ extends ListenerService<VirtualNetworkEvent, VirtualNetworkListener> {
+
+ /**
+ * The topic used for obtaining globally unique ids.
+ */
+ String VIRTUAL_NETWORK_TOPIC = "virtual-network-ids";
+
+ /**
+ * Returns a collection of all virtual networks created on behalf of the
+ * specified tenant.
+ *
+ * @param tenantId tenant identifier
+ * @return collection of networks
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId);
+
+ /**
+ * Returns the virtual network matching the network identifier.
+ *
+ * @param networkId virtual network identifier
+ * @return virtual network instance
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ VirtualNetwork getVirtualNetwork(NetworkId networkId);
+
+ /**
+ * Returns {@code tenantId} for specified virtual network id.
+ *
+ * @param networkId virtual network identifier
+ * @return tenantId tenant identifier
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ TenantId getTenantId(NetworkId networkId);
+
+ /**
+ * Returns a collection of all virtual devices in the specified network.
+ *
+ * @param networkId network identifier
+ * @return collection of devices
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ Set<VirtualDevice> getVirtualDevices(NetworkId networkId);
+
+ /**
+ * Returns a collection of all virtual hosts in the specified network.
+ *
+ * @param networkId network identifier
+ * @return collection of hosts
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ Set<VirtualHost> getVirtualHosts(NetworkId networkId);
+
+ /**
+ * Returns collection of all virtual links in the specified network.
+ *
+ * @param networkId network identifier
+ * @return collection of links
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ Set<VirtualLink> getVirtualLinks(NetworkId networkId);
+
+ /**
+ * Returns list of all virtual ports of the specified device. If the
+ * device identifier is null then all of the virtual ports in the specified
+ * network will be returned.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @return list of ports
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ */
+ Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns list of physical device identifier mapping with the virtual
+ * device in the specified network. The physical devices are specified by
+ * port mapping mechanism.
+ *
+ * @param networkId network identifier
+ * @param deviceId the virtual device identifier
+ * @return collection of the specified device's identifier
+ */
+ Set<DeviceId> getPhysicalDevices(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Returns implementation of the specified service class for operating
+ * in the context of the given network.
+ * <p>
+ * The following services will be available:
+ * <ul>
+ * <li>{@link org.onosproject.net.device.DeviceService}</li>
+ * <li>{@link org.onosproject.net.link.LinkService}</li>
+ * <li>{@link org.onosproject.net.host.HostService}</li>
+ * <li>{@link org.onosproject.net.topology.TopologyService}</li>
+ * <li>{@link org.onosproject.net.topology.PathService}</li>
+ * <li>{@link org.onosproject.net.packet.PacketService}</li>
+ * <li>{@link org.onosproject.net.flow.FlowRuleService}</li>
+ * <li>{@link org.onosproject.net.flowobjective.FlowObjectiveService}</li>
+ * <li>{@link org.onosproject.net.intent.IntentService}</li>
+ * <li>{@link org.onosproject.mastership.MastershipService}</li>
+ * <li>{@link org.onosproject.mastership.MastershipAdminService}</li>
+ * <li>{@link org.onosproject.mastership.MastershipTermService}</li>
+ * </ul>
+ *
+ * @param networkId network identifier
+ * @param serviceClass service class
+ * @param <T> type of service
+ * @return implementation class
+ * @throws org.onlab.util.ItemNotFoundException if no such network found
+ * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
+ */
+ <T> T get(NetworkId networkId, Class<T> serviceClass);
+
+ /**
+ * Returns service directory.
+ *
+ * @return a service directory
+ */
+ ServiceDirectory getServiceDirectory();
+
+ /**
+ * Returns the application identifier for a virtual network.
+ *
+ * @param networkId network identifier
+ * @return an representative application identifier for a virtual network
+ */
+ ApplicationId getVirtualNetworkApplicationId(NetworkId networkId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
new file mode 100644
index 0000000..de74333
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2015-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;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TenantId;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.store.Store;
+
+import java.util.Set;
+
+/**
+ * Mechanism for distributing and storing virtual network model information.
+ */
+public interface VirtualNetworkStore
+ extends Store<VirtualNetworkEvent, VirtualNetworkStoreDelegate> {
+
+ /**
+ * Adds a new tenant ID to the store.
+ *
+ * @param tenantId tenant identifier
+ */
+ void addTenantId(TenantId tenantId);
+
+ /**
+ * Removes the specified tenant ID from the store.
+ *
+ * @param tenantId tenant identifier
+ */
+ void removeTenantId(TenantId tenantId);
+
+ /**
+ * Returns set of registered tenant IDs.
+ *
+ * @return set of tenant identifiers
+ */
+ Set<TenantId> getTenantIds();
+
+ /**
+ * Adds a new virtual network for the specified tenant to the store.
+ *
+ * @param tenantId tenant identifier
+ * @return the virtual network
+ */
+ VirtualNetwork addNetwork(TenantId tenantId);
+
+ /**
+ * Removes the specified virtual network from the store.
+ *
+ * @param networkId network identifier
+ */
+ void removeNetwork(NetworkId networkId);
+
+ /**
+ * Adds a new virtual device to the store. This device will have no ports.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @return the virtual device
+ */
+ VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Removes the specified virtual device from the given network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ */
+ void removeDevice(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Adds a new virtual host to the store.
+ *
+ * @param networkId network identifier
+ * @param hostId host identifier
+ * @param mac mac address
+ * @param vlan vlan identifier
+ * @param location host location
+ * @param ips set of ip addresses
+ * @return the virtual host
+ */
+ VirtualHost addHost(NetworkId networkId, HostId hostId, MacAddress mac,
+ VlanId vlan, HostLocation location, Set<IpAddress> ips);
+
+ /**
+ * Removes the specified virtual host from the store.
+ *
+ * @param networkId network identifier
+ * @param hostId host identifier
+ */
+ void removeHost(NetworkId networkId, HostId hostId);
+
+ /**
+ * Adds a new virtual link.
+ *
+ * @param networkId network identifier
+ * @param src source end-point of the link
+ * @param dst destination end-point of the link
+ * @param state link state
+ * @param realizedBy underlying tunnel identifier using which this link is realized
+ * @return the virtual link
+ */
+ VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, Link.State state, TunnelId realizedBy);
+
+ /**
+ * Updates the tunnelId in the virtual link.
+ *
+ * @param virtualLink virtual link
+ * @param tunnelId tunnel identifier
+ * @param state link state
+ */
+ void updateLink(VirtualLink virtualLink, TunnelId tunnelId, Link.State state);
+
+ /**
+ * Removes the specified link from the store.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @return the virtual link
+ */
+ VirtualLink removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+ /**
+ * Adds a new virtual port to the network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @param portNumber port number
+ * @param realizedBy underlying port which realizes the virtual port
+ * @return the virtual port
+ */
+ VirtualPort addPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy);
+
+ /**
+ * Binds an existing virtual port to the network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @param portNumber port number
+ * @param realizedBy underlying port which realizes the virtual port
+ */
+ void bindPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy);
+
+ /**
+ * Updates port state of an existing virtual port.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @param portNumber port number
+ * @param isEnabled indicator whether the port is up and active
+ */
+ void updatePortState(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, boolean isEnabled);
+
+ /**
+ * Removes the specified port from the given device and network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @param portNumber port number
+ */
+ void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber);
+
+ /**
+ * Returns the list of networks.
+ *
+ * @param tenantId tenant identifier
+ * @return set of virtual networks
+ */
+ Set<VirtualNetwork> getNetworks(TenantId tenantId);
+
+ /**
+ * Returns the virtual network for the given network identifier.
+ *
+ * @param networkId network identifier
+ * @return the virtual network
+ */
+ VirtualNetwork getNetwork(NetworkId networkId);
+
+ /**
+ * Returns the list of devices in the specified virtual network.
+ *
+ * @param networkId network identifier
+ * @return set of virtual devices
+ */
+ Set<VirtualDevice> getDevices(NetworkId networkId);
+
+ /**
+ * Returns the list of hosts in the specified virtual network.
+ *
+ * @param networkId network identifier
+ * @return set of virtual hosts
+ */
+ Set<VirtualHost> getHosts(NetworkId networkId);
+
+ /**
+ * Returns the list of virtual links in the specified virtual network.
+ *
+ * @param networkId network identifier
+ * @return set of virtual links
+ */
+ Set<VirtualLink> getLinks(NetworkId networkId);
+
+ /**
+ * Returns the virtual link matching the network identifier, source connect point,
+ * and destination connect point.
+ *
+ * @param networkId network identifier
+ * @param src source connect point
+ * @param dst destination connect point
+ * @return virtual link
+ */
+ VirtualLink getLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+ /**
+ * Returns the list of ports of the specified virtual device.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @return set of virtual networks
+ */
+ Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId);
+
+ /**
+ * Adds the intent to tunnel identifier mapping to the store.
+ *
+ * @param intent intent
+ * @param tunnelId tunnel identifier
+ * @deprecated in Kingfisher Release (1.10)
+ */
+ @Deprecated
+ void addTunnelId(Intent intent, TunnelId tunnelId);
+
+ /**
+ * Return the set of tunnel identifiers store against the intent.
+ *
+ * @param intent intent
+ * @return set of tunnel identifiers
+ * @deprecated in Kingfisher Release (1.10)
+ */
+ @Deprecated
+ Set<TunnelId> getTunnelIds(Intent intent);
+
+ /**
+ * Removes the intent to tunnel identifier mapping from the store.
+ *
+ * @param intent intent
+ * @param tunnelId tunnel identifier
+ * @deprecated in Kingfisher Release (1.10)
+ */
+ @Deprecated
+ void removeTunnelId(Intent intent, TunnelId tunnelId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java
new file mode 100644
index 0000000..202a0be
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015-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;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * Network configuration store delegate abstraction.
+ */
+public interface VirtualNetworkStoreDelegate extends StoreDelegate<VirtualNetworkEvent> {
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java
new file mode 100644
index 0000000..42b548a
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPacketContext.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015-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;
+
+import org.onosproject.net.packet.PacketContext;
+
+/**
+ * Represents context for processing an inbound packet for a virtual network,
+ * and (optionally) emitting a corresponding outbound packet.
+ */
+public interface VirtualPacketContext extends PacketContext {
+ /**
+ * Returns the network identifier.
+ *
+ * @return network id
+ */
+ NetworkId networkId();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPort.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPort.java
new file mode 100644
index 0000000..a393e8e
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPort.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Port;
+
+/**
+ * Representation of a virtual port.
+ */
+@Beta
+public interface VirtualPort extends VirtualElement, Port {
+
+ /**
+ * Returns the underlying port using which this port is realized.
+ *
+ * @return underlying port which realizes this virtual port
+ */
+ ConnectPoint realizedBy();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPortDescription.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPortDescription.java
new file mode 100644
index 0000000..0785641
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualPortDescription.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;
+
+import org.onosproject.net.device.PortDescription;
+
+/**
+ * Information about a virtual port.
+ */
+public interface VirtualPortDescription extends PortDescription {
+
+ // TODO: Add something about a requirement of virtual port.
+
+ /**
+ * Representation of the virtual port.
+ */
+ enum State {
+ /**
+ * Signifies that a virtual port is currently start.
+ */
+ START,
+
+ /**
+ * Signifies that a virtual port is currently stop.
+ */
+ STOP,
+
+ /**
+ * Signifies that a virtual port is pause for a moment.
+ */
+ PAUSE
+ }
+
+ /**
+ * Starts the virtual port.
+ */
+ void start();
+
+ /**
+ * Stops the virtual port.
+ */
+ void stop();
+
+ /**
+ * Pauses the virtual port for stopping a network or device.
+ * e.g. snapshot.
+ */
+ void pause();
+
+ /**
+ * Returns the virtual port state.
+ *
+ * @return state of virtual port
+ */
+ State state();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualStore.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualStore.java
new file mode 100644
index 0000000..8a25874
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualStore.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;
+
+import org.onosproject.event.Event;
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * Abstraction of a entity capable of storing and/or distributing information
+ * for virtual network across a cluster.
+ */
+public interface VirtualStore<E extends Event, D extends StoreDelegate<E>> {
+ /**
+ * Sets the delegate on the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param delegate new store delegate
+ * @throws java.lang.IllegalStateException if a delegate is already
+ * currently set on the store and is a different one that
+ */
+ void setDelegate(NetworkId networkId, D delegate);
+
+ /**
+ * Withdraws the delegate from the store.
+ *
+ * @param networkId a virtual network identifier
+ * @param delegate store delegate to withdraw
+ * @throws java.lang.IllegalArgumentException if the delegate is not
+ * currently set on the store
+ */
+ void unsetDelegate(NetworkId networkId, D delegate);
+
+ /**
+ * Indicates whether the store has a delegate.
+ *
+ * @param networkId a virtual network identifier
+ * @return true if delegate is set
+ */
+ boolean hasDelegate(NetworkId networkId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VnetService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VnetService.java
new file mode 100644
index 0000000..1889ad4
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/VnetService.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Virtual network service interface.
+ */
+public interface VnetService {
+ NetworkId networkId();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualDeviceCodec.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualDeviceCodec.java
new file mode 100644
index 0000000..c0c8cf8
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualDeviceCodec.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018-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.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.net.DeviceId;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec for the VirtualDevice class.
+ */
+public class VirtualDeviceCodec extends JsonCodec<VirtualDevice> {
+
+ // JSON field names
+ private static final String ID = "deviceId";
+ private static final String NETWORK_ID = "networkId";
+
+ private static final String NULL_OBJECT_MSG = "VirtualDevice cannot be null";
+ private static final String MISSING_MEMBER_MSG = " member is required in VirtualDevice";
+
+ @Override
+ public ObjectNode encode(VirtualDevice vDev, CodecContext context) {
+ checkNotNull(vDev, NULL_OBJECT_MSG);
+
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(NETWORK_ID, vDev.networkId().toString())
+ .put(ID, vDev.id().toString());
+
+ return result;
+ }
+
+ @Override
+ public VirtualDevice decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ DeviceId dId = DeviceId.deviceId(extractMember(ID, json));
+ NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
+ return new DefaultVirtualDevice(nId, dId);
+ }
+
+ /**
+ * Extract member from JSON ObjectNode.
+ *
+ * @param key key for which value is needed
+ * @param json JSON ObjectNode
+ * @return member value
+ */
+ private String extractMember(String key, ObjectNode json) {
+ return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodec.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodec.java
new file mode 100644
index 0000000..2ddb589
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodec.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2018-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.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.DefaultVirtualHost;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec for the VirtualHost class.
+ */
+public class VirtualHostCodec extends JsonCodec<VirtualHost> {
+
+ // JSON field names
+ static final String NETWORK_ID = "networkId";
+ static final String HOST_ID = "id";
+ static final String MAC_ADDRESS = "mac";
+ static final String VLAN = "vlan";
+ static final String IP_ADDRESSES = "ipAddresses";
+ static final String HOST_LOCATION = "locations";
+
+ private static final String NULL_OBJECT_MSG = "VirtualHost cannot be null";
+ private static final String MISSING_MEMBER_MSG = " member is required in VirtualHost";
+
+ @Override
+ public ObjectNode encode(VirtualHost vHost, CodecContext context) {
+ checkNotNull(vHost, NULL_OBJECT_MSG);
+
+ final JsonCodec<HostLocation> locationCodec =
+ context.codec(HostLocation.class);
+ final ObjectNode result = context.mapper().createObjectNode()
+ .put(NETWORK_ID, vHost.networkId().toString())
+ .put(HOST_ID, vHost.id().toString())
+ .put(MAC_ADDRESS, vHost.mac().toString())
+ .put(VLAN, vHost.vlan().toString());
+
+ final ArrayNode jsonIpAddresses = result.putArray(IP_ADDRESSES);
+ for (final IpAddress ipAddress : vHost.ipAddresses()) {
+ jsonIpAddresses.add(ipAddress.toString());
+ }
+ result.set(IP_ADDRESSES, jsonIpAddresses);
+
+ final ArrayNode jsonLocations = result.putArray("locations");
+ for (final HostLocation location : vHost.locations()) {
+ jsonLocations.add(locationCodec.encode(location, context));
+ }
+ result.set("locations", jsonLocations);
+
+ return result;
+ }
+
+ @Override
+ public VirtualHost decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
+ MacAddress mac = MacAddress.valueOf(json.get("mac").asText());
+ VlanId vlanId = VlanId.vlanId((short) json.get("vlan").asInt(VlanId.UNTAGGED));
+
+ Set<HostLocation> locations = new HashSet<>();
+ JsonNode locationNodes = json.get("locations");
+ locationNodes.forEach(locationNode -> {
+ PortNumber portNumber = PortNumber.portNumber(locationNode.get("port").asText());
+ DeviceId deviceId = DeviceId.deviceId(locationNode.get("elementId").asText());
+ locations.add(new HostLocation(deviceId, portNumber, 0));
+ });
+
+ HostId id = HostId.hostId(mac, vlanId);
+
+ Iterator<JsonNode> ipStrings = json.get("ipAddresses").elements();
+ Set<IpAddress> ips = new HashSet<>();
+ while (ipStrings.hasNext()) {
+ ips.add(IpAddress.valueOf(ipStrings.next().asText()));
+ }
+
+ return new DefaultVirtualHost(nId, id, mac, vlanId, locations, ips);
+ }
+
+ /**
+ * Extract member from JSON ObjectNode.
+ *
+ * @param key key for which value is needed
+ * @param json JSON ObjectNode
+ * @return member value
+ */
+ private String extractMember(String key, ObjectNode json) {
+ return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualLinkCodec.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualLinkCodec.java
new file mode 100644
index 0000000..641982f
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualLinkCodec.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018-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.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.net.Link;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec for the VirtualLink class.
+ */
+public class VirtualLinkCodec extends JsonCodec<VirtualLink> {
+
+ // JSON field names
+ private static final String NETWORK_ID = "networkId";
+
+ private static final String NULL_OBJECT_MSG = "VirtualLink cannot be null";
+ private static final String MISSING_MEMBER_MSG = " member is required in VirtualLink";
+
+ @Override
+ public ObjectNode encode(VirtualLink vLink, CodecContext context) {
+ checkNotNull(vLink, NULL_OBJECT_MSG);
+
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(NETWORK_ID, vLink.networkId().toString());
+ JsonCodec<Link> codec = context.codec(Link.class);
+ ObjectNode linkResult = codec.encode(vLink, context);
+ result.setAll(linkResult);
+ return result;
+ }
+
+ @Override
+ public VirtualLink decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+ JsonCodec<Link> codec = context.codec(Link.class);
+ Link link = codec.decode(json, context);
+ NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
+ return DefaultVirtualLink.builder()
+ .networkId(nId)
+ .src(link.src())
+ .dst(link.dst())
+ .build();
+ }
+
+ /**
+ * Extract member from JSON ObjectNode.
+ *
+ * @param key key for which value is needed
+ * @param json JSON ObjectNode
+ * @return member value
+ */
+ private String extractMember(String key, ObjectNode json) {
+ return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualNetworkCodec.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualNetworkCodec.java
new file mode 100644
index 0000000..32945e4
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualNetworkCodec.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018-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.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec for the VirtualNetwork class.
+ */
+public class VirtualNetworkCodec extends JsonCodec<VirtualNetwork> {
+
+ // JSON field names
+ private static final String NETWORK_ID = "networkId";
+ private static final String TENANT_ID = "tenantId";
+
+ private static final String NULL_OBJECT_MSG = "VirtualNetwork cannot be null";
+ private static final String MISSING_MEMBER_MSG = " member is required in VirtualNetwork";
+
+ @Override
+ public ObjectNode encode(VirtualNetwork vnet, CodecContext context) {
+ checkNotNull(vnet, NULL_OBJECT_MSG);
+
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(NETWORK_ID, vnet.id().toString())
+ .put(TENANT_ID, vnet.tenantId().toString());
+
+ return result;
+ }
+
+ @Override
+ public VirtualNetwork decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
+ TenantId tId = TenantId.tenantId(extractMember(TENANT_ID, json));
+ return new DefaultVirtualNetwork(nId, tId);
+ }
+
+ private String extractMember(String key, ObjectNode json) {
+ return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualPortCodec.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualPortCodec.java
new file mode 100644
index 0000000..474196e
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/VirtualPortCodec.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018-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.codec;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+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 java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Codec for the VirtualPort class.
+ */
+public class VirtualPortCodec extends JsonCodec<VirtualPort> {
+
+ // JSON field names
+ private static final String NETWORK_ID = "networkId";
+ private static final String DEVICE_ID = "deviceId";
+ private static final String PORT_NUM = "portNum";
+ private static final String PHYS_DEVICE_ID = "physDeviceId";
+ private static final String PHYS_PORT_NUM = "physPortNum";
+
+ private static final String NULL_OBJECT_MSG = "VirtualPort cannot be null";
+ private static final String MISSING_MEMBER_MSG = " member is required in VirtualPort";
+ private static final String INVALID_VIRTUAL_DEVICE = " is not a valid VirtualDevice";
+
+ @Override
+ public ObjectNode encode(VirtualPort vPort, CodecContext context) {
+ checkNotNull(vPort, NULL_OBJECT_MSG);
+
+ ObjectNode result = context.mapper().createObjectNode()
+ .put(NETWORK_ID, vPort.networkId().toString())
+ .put(DEVICE_ID, vPort.element().id().toString())
+ .put(PORT_NUM, vPort.number().toString())
+ .put(PHYS_DEVICE_ID, vPort.realizedBy().deviceId().toString())
+ .put(PHYS_PORT_NUM, vPort.realizedBy().port().toString());
+
+ return result;
+ }
+
+ @Override
+ public VirtualPort decode(ObjectNode json, CodecContext context) {
+ if (json == null || !json.isObject()) {
+ return null;
+ }
+
+ NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
+ DeviceId dId = DeviceId.deviceId(extractMember(DEVICE_ID, json));
+
+ VirtualNetworkService vnetService = context.getService(VirtualNetworkService.class);
+ Set<VirtualDevice> vDevs = vnetService.getVirtualDevices(nId);
+ VirtualDevice vDev = vDevs.stream()
+ .filter(virtualDevice -> virtualDevice.id().equals(dId))
+ .findFirst().orElse(null);
+ nullIsIllegal(vDev, dId.toString() + INVALID_VIRTUAL_DEVICE);
+
+ PortNumber portNum = PortNumber.portNumber(extractMember(PORT_NUM, json));
+ DeviceId physDId = DeviceId.deviceId(extractMember(PHYS_DEVICE_ID, json));
+ PortNumber physPortNum = PortNumber.portNumber(extractMember(PHYS_PORT_NUM, json));
+
+ ConnectPoint realizedBy = new ConnectPoint(physDId, physPortNum);
+ return new DefaultVirtualPort(nId, vDev, portNum, realizedBy);
+ }
+
+ private String extractMember(String key, ObjectNode json) {
+ return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/package-info.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/package-info.java
new file mode 100644
index 0000000..8999592
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/codec/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * Network virtualization data model codecs.
+ */
+package org.onosproject.incubator.net.virtual.codec;
\ No newline at end of file
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/AbstractVirtualListenerManager.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/AbstractVirtualListenerManager.java
new file mode 100644
index 0000000..57dfe0c
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/AbstractVirtualListenerManager.java
@@ -0,0 +1,91 @@
+/*
+ * 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.event;
+
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.event.EventListener;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.event.ListenerService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VnetService;
+
+/**
+ * Basis for virtual event components which need to export listener mechanism.
+ */
+public abstract class AbstractVirtualListenerManager
+ <E extends Event, L extends EventListener<E>>
+ implements ListenerService<E, L>, VnetService {
+
+ protected final NetworkId networkId;
+ protected final VirtualNetworkService manager;
+ protected final ServiceDirectory serviceDirectory;
+
+ protected EventDeliveryService eventDispatcher;
+
+ private ListenerRegistry<E, L> listenerRegistry;
+
+ private VirtualListenerRegistryManager listenerManager =
+ VirtualListenerRegistryManager.getInstance();
+
+ public AbstractVirtualListenerManager(VirtualNetworkService manager,
+ NetworkId networkId,
+ Class<? extends Event> eventClass) {
+ this.manager = manager;
+ this.networkId = networkId;
+ this.serviceDirectory = manager.getServiceDirectory();
+
+ //Set default event delivery service by default
+ this.eventDispatcher = serviceDirectory.get(EventDeliveryService.class);
+
+ //Initialize and reference to the listener registry
+ this.listenerRegistry = listenerManager.getRegistry(networkId, eventClass);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void addListener(L listener) {
+ listenerRegistry.addListener(listener);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void removeListener(L listener) {
+ listenerRegistry.removeListener(listener);
+ }
+
+ /**
+ * Safely posts the specified event to the local event dispatcher.
+ * If there is no event dispatcher or if the event is null, this method
+ * is a noop.
+ *
+ * @param event event to be posted; may be null
+ */
+ protected void post(E event) {
+ if (event != null && eventDispatcher != null) {
+ VirtualEvent<E> vEvent =
+ new VirtualEvent<E>(networkId, VirtualEvent.Type.POSTED, event);
+ eventDispatcher.post(vEvent);
+ }
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return this.networkId;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualEvent.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualEvent.java
new file mode 100644
index 0000000..bbba983
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualEvent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.event;
+
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.virtual.NetworkId;
+
+/**
+ * Base class for virtual network event that encapsulates a normal event.
+ */
+public class VirtualEvent<E extends Event>
+ extends AbstractEvent<VirtualEvent.Type, E> {
+
+ private NetworkId networkId;
+
+ /**
+ * Type of virtual network events.
+ */
+ public enum Type {
+ /**
+ * A new virtual event has been posted.
+ */
+ POSTED
+ }
+
+ protected VirtualEvent(NetworkId networkId, Type type, E subject) {
+ super(type, subject);
+ this.networkId = networkId;
+ }
+
+ protected VirtualEvent(NetworkId networkId, Type type, E subject, long time) {
+ super(type, subject, time);
+ this.networkId = networkId;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualListenerRegistryManager.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualListenerRegistryManager.java
new file mode 100644
index 0000000..55210dd
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/VirtualListenerRegistryManager.java
@@ -0,0 +1,99 @@
+/*
+ * 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.event;
+
+import com.google.common.collect.Maps;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventSink;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.incubator.net.virtual.NetworkId;
+
+import java.util.Map;
+
+/**
+ * Base implementation of an virtual event sink and a registry capable of tracking
+ * listeners and dispatching events to them as part of event sink processing.
+ */
+public final class VirtualListenerRegistryManager
+ implements EventSink<VirtualEvent> {
+
+ private Map<NetworkId, Map<Class<? extends Event>, ListenerRegistry>>
+ listenerMapByNetwork = Maps.newConcurrentMap();
+
+ ListenerRegistry lastStart;
+
+ // non-instantiable (except for our Singleton)
+ private VirtualListenerRegistryManager() {
+
+ }
+
+ public static VirtualListenerRegistryManager getInstance() {
+ return SingletonHelper.INSTANCE;
+ }
+
+ public ListenerRegistry getRegistry(NetworkId networkId,
+ Class<? extends Event> eventClass) {
+ Map<Class<? extends Event>, ListenerRegistry> listenerMapByEvent =
+ listenerMapByNetwork.get(networkId);
+
+ if (listenerMapByEvent == null) {
+ listenerMapByEvent = Maps.newConcurrentMap();
+ listenerMapByNetwork.putIfAbsent(networkId, listenerMapByEvent);
+ }
+
+ ListenerRegistry listenerRegistry = listenerMapByEvent.get(eventClass);
+
+ if (listenerRegistry == null) {
+ listenerRegistry = new ListenerRegistry();
+ listenerMapByEvent.putIfAbsent(eventClass, listenerRegistry);
+ }
+
+ return listenerRegistry;
+ }
+
+ @Override
+ public void process(VirtualEvent event) {
+ NetworkId networkId = event.networkId();
+ Event originalEvent = (Event) event.subject();
+
+ ListenerRegistry listenerRegistry =
+ listenerMapByNetwork.get(networkId).get(originalEvent.getClass());
+ if (listenerRegistry != null) {
+ listenerRegistry.process(originalEvent);
+ lastStart = listenerRegistry;
+ }
+ }
+
+ @Override
+ public void onProcessLimit() {
+ lastStart.onProcessLimit();
+ }
+
+ /**
+ * Prevents object instantiation from external.
+ */
+ private static final class SingletonHelper {
+ private static final String ILLEGAL_ACCESS_MSG =
+ "Should not instantiate this class.";
+ private static final VirtualListenerRegistryManager INSTANCE =
+ new VirtualListenerRegistryManager();
+
+ private SingletonHelper() {
+ throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
+ }
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/package-info.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/package-info.java
new file mode 100644
index 0000000..448df90
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/event/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Virtual network event delivery subsystem interfaces & supporting abstractions.
+ */
+package org.onosproject.incubator.net.virtual.event;
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java
new file mode 100644
index 0000000..e76bf6e
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.intent;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.intent.Intent;
+
+import java.util.List;
+
+/**
+ * Abstraction of a compiler which is capable of taking an intent
+ * and translating it to other, potentially installable, intents for virtual networks.
+ *
+ * @param <T> the type of intent
+ */
+@Beta
+public interface VirtualIntentCompiler<T extends Intent> {
+ /**
+ * Compiles the specified intent into other intents.
+ *
+ * @param networkId network identifier
+ * @param intent intent to be compiled
+ * @param installable previous compilation result; optional
+ * @return list of resulting intents
+ * @throws VirtualIntentException if issues are encountered while compiling the intent
+ */
+ List<Intent> compile(NetworkId networkId, T intent, List<Intent> installable);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java
new file mode 100644
index 0000000..49cb908
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.intent;
+
+public class VirtualIntentException extends RuntimeException {
+
+ //FIXME: how to obatin UID?
+ private static final long serialVersionUID = 1907263634145241319L;
+
+ /**
+ * Constructs an exception with no message and no underlying cause.
+ */
+ public VirtualIntentException() {
+ }
+
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public VirtualIntentException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public VirtualIntentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java
new file mode 100644
index 0000000..c5673ac
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Set of abstractions for conveying high-level intents for treatment of
+ * selected network traffic by allowing applications to express the
+ * <em>what</em> rather than the <em>how</em> for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.intent;
\ No newline at end of file
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/package-info.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/package-info.java
new file mode 100644
index 0000000..d9ced32
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/**
+ * Network virtualization data models and services.
+ */
+package org.onosproject.incubator.net.virtual;
\ No newline at end of file
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java
new file mode 100644
index 0000000..bb4ba30
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.provider.ProviderId;
+
+public abstract class AbstractVirtualProvider implements VirtualProvider {
+ private final ProviderId providerId;
+
+ /**
+ * Creates a virtual provider with the supplied identifier.
+ *
+ * @param id a virtual provider id
+ */
+ protected AbstractVirtualProvider(ProviderId id) {
+ this.providerId = id;
+ }
+
+ @Override
+ public ProviderId id() {
+ return providerId;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java
new file mode 100644
index 0000000..2411e26
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.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.provider;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Base implementation of a virtual provider service,
+ * which tracks the provider to which it is issued and can be invalidated.
+ *
+ * @param <P> type of the information provider
+ */
+public abstract class AbstractVirtualProviderService<P extends VirtualProvider>
+ implements VirtualProviderService<P> {
+
+ private boolean isValid = true;
+ private P provider = null;
+
+ /**
+ * Creates a virtual provider service on behalf of the specified provider.
+ *
+ * @param provider provider to which this service is being issued
+ */
+ protected void setProvider(P provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Invalidates this provider service.
+ */
+ public void invalidate() {
+ isValid = false;
+ }
+
+ /**
+ * Checks the validity of this provider service.
+ *
+ * @throws java.lang.IllegalStateException if the service is no longer valid
+ */
+ public void checkValidity() {
+ checkState(isValid, "Provider service is no longer valid");
+ }
+
+ @Override
+ public P provider() {
+ return provider;
+ }
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/InternalRoutingAlgorithm.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/InternalRoutingAlgorithm.java
new file mode 100644
index 0000000..c17f0d9
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/InternalRoutingAlgorithm.java
@@ -0,0 +1,36 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Path;
+
+/**
+ * Abstraction of an embedding algorithm used for embedding virtual objects
+ * into the underlying physical network.
+ */
+public interface InternalRoutingAlgorithm {
+ /**
+ * Find a route between two connect points (i.e. ports)
+ * according to the own logic.
+ *
+ * @param src A start point
+ * @param dst A sink point
+ * @return The path between src and dst calculated from the algorithm
+ */
+ Path findPath(ConnectPoint src, ConnectPoint dst);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProvider.java
new file mode 100644
index 0000000..9191eaf
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProvider.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.provider;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Abstraction of a virtual device information provider.
+ */
+public interface VirtualDeviceProvider extends VirtualProvider {
+
+ /**
+ * Notifies the provider of a mastership role change for the specified
+ * device as decided by the core.
+ *
+ * @param deviceId device identifier
+ * @param newRole newly determined mastership role
+ */
+ void roleChanged(DeviceId deviceId, MastershipRole newRole);
+
+ /**
+ * Indicates whether or not the specified connect points on the underlying
+ * network are traversable.
+ *
+ * @param src source connection point
+ * @param dst destination connection point
+ * @return true if the destination is reachable from the source
+ */
+ boolean isTraversable(ConnectPoint src, ConnectPoint dst);
+
+ /**
+ * Indicates whether or not the all physical devices mapped by the given
+ * virtual device are reachable.
+ *
+ * @param deviceId device identifier
+ * @return true if the all physical devices are reachable, false otherwise
+ */
+ boolean isReachable(DeviceId deviceId);
+
+ /**
+ * Administratively enables or disables a port.
+ *
+ * @param deviceId device identifier
+ * @param portNumber port number
+ * @param enable true if port is to be enabled, false to disable
+ */
+ void changePortState(DeviceId deviceId, PortNumber portNumber,
+ boolean enable);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProviderService.java
new file mode 100644
index 0000000..e2d584c
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualDeviceProviderService.java
@@ -0,0 +1,69 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.VirtualPortDescription;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.PortStatistics;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Service through which virtual device providers can inject virtual device
+ * information into the core.
+ */
+public interface VirtualDeviceProviderService
+ extends VirtualProviderService<VirtualDeviceProvider> {
+
+ /**
+ * Updates information about all ports of a device. It is up to the core to
+ * determine what has changed.
+ *
+ * @param deviceId identity of the device
+ * @param portDescs list of virtual device ports
+ */
+ void updatePorts(DeviceId deviceId, List<VirtualPortDescription> portDescs);
+
+ /**
+ * Notifies the core about port status change of a single port.
+ *
+ * @param deviceId identity of the device
+ * @param portDesc description of the virtual port that changed
+ */
+ void portStatusChanged(DeviceId deviceId, VirtualPortDescription portDesc);
+
+ /**
+ * Notifies the core about the result of a RoleRequest sent to a device.
+ *
+ * @param deviceId identity of the device
+ * @param requested mastership role that was requested by the node
+ * @param response mastership role the switch accepted
+ */
+ void receivedRoleReply(DeviceId deviceId, MastershipRole requested,
+ MastershipRole response);
+
+ /**
+ * Updates statistics about all ports of a device.
+ *
+ * @param deviceId identity of the device
+ * @param portStatistics list of device port statistics
+ */
+ void updatePortStatistics(DeviceId deviceId,
+ Collection<PortStatistics> portStatistics);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProvider.java
new file mode 100644
index 0000000..8a4ec34
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+
+/**
+ * Abstraction of a virtual flow rule provider.
+ * This provider virtualizes and de-virtualizes FlowRule.
+ * See {@link org.onosproject.net.flow.FlowRule}.
+ */
+public interface VirtualFlowRuleProvider extends VirtualProvider {
+
+ /**
+ * Instructs the provider to apply the specified flow rules to their
+ * respective virtual devices.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param flowRules one or more flow rules
+ */
+ void applyFlowRule(NetworkId networkId, FlowRule... flowRules);
+
+ /**
+ * Instructs the provider to remove the specified flow rules to their
+ * respective virtual devices.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param flowRules one or more flow rules
+ */
+ void removeFlowRule(NetworkId networkId, FlowRule... flowRules);
+
+ /**
+ * Installs a batch of flow rules. Each flowrule is associated to an
+ * operation which results in either addition, removal or modification.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param batch a batch of flow rules
+ */
+ void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProviderService.java
new file mode 100644
index 0000000..065af9c
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualFlowRuleProviderService.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.provider;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
+
+import java.util.List;
+
+/**
+ * Service through which virtual flow rule providers can inject information into
+ * the core.
+ */
+public interface VirtualFlowRuleProviderService
+ extends VirtualProviderService<VirtualFlowRuleProvider> {
+
+ /**
+ * Signals that a flow rule that was previously installed has been removed.
+ *
+ * @param flowEntry removed flow entry
+ */
+ void flowRemoved(FlowEntry flowEntry);
+
+ /**
+ * Pushes the collection of flow entries currently applied on the given
+ * virtual device.
+ *
+ * @param deviceId device identifier
+ * @param flowEntries collection of flow rules
+ */
+ void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
+
+ /**
+ * Pushes the collection of flow entries currently applied on the given
+ * device without flowMissing process.
+ *
+ * @param deviceId device identifier
+ * @param flowEntries collection of flow rules
+ */
+ void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
+
+ /**
+ * Pushes the collection of table statistics entries currently extracted
+ * from the given virtual device.
+ *
+ * @param deviceId device identifier
+ * @param tableStatsEntries collection of flow table statistics entries
+ */
+ void pushTableStatistics(DeviceId deviceId, List<TableStatisticsEntry> tableStatsEntries);
+
+ /**
+ * Indicates to the core that the requested batch operation has
+ * been completed.
+ *
+ * @param batchId the batch which was processed
+ * @param operation the resulting outcome of the operation
+ */
+ void batchOperationCompleted(long batchId, CompletedBatchOperation operation);
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProvider.java
new file mode 100644
index 0000000..72d68c3
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.GroupOperations;
+
+/**
+ * Abstraction of group provider for virtual network.
+ */
+public interface VirtualGroupProvider extends VirtualProvider {
+ /**
+ * Performs a batch of group operation in the specified virtual device
+ * with the specified parameters.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param deviceId device identifier on which the batch of group
+ * operations to be executed
+ * @param groupOps immutable list of group operation
+ */
+ void performGroupOperation(NetworkId networkId, DeviceId deviceId,
+ GroupOperations groupOps);
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProviderService.java
new file mode 100644
index 0000000..0fdaca8
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualGroupProviderService.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.provider;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupOperation;
+
+import java.util.Collection;
+
+/**
+ * Service through which Group providers can inject information into
+ * the virtual network core.
+ */
+public interface VirtualGroupProviderService
+ extends VirtualProviderService<VirtualGroupProvider> {
+ /**
+ * Notifies core if any failure from data plane during group operations.
+ *
+ * @param deviceId the device ID
+ * @param operation offended group operation
+ */
+ void groupOperationFailed(DeviceId deviceId, GroupOperation operation);
+
+ /**
+ * Pushes the collection of group detected in the data plane along
+ * with statistics.
+ *
+ * @param deviceId device identifier
+ * @param groupEntries collection of group entries as seen in data plane
+ */
+ void pushGroupMetrics(DeviceId deviceId, Collection<Group> groupEntries);
+
+ /**
+ * Notifies store of group failovers.
+ *
+ * @param failoverGroups failover groups in which a failover has occurred
+ */
+ void notifyOfFailovers(Collection<Group> failoverGroups);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProvider.java
new file mode 100644
index 0000000..6ac80e4
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProvider.java
@@ -0,0 +1,52 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+
+/**
+ * Abstraction of a Meter provider for virtual network.
+ */
+public interface VirtualMeterProvider extends VirtualProvider {
+ /**
+ * Performs a batch of meter operation on the specified virtual device
+ * with the specified parameters.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param deviceId device identifier on which the batch of group
+ * operations to be executed
+ * @param meterOps immutable list of meter operation
+ */
+ void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperations meterOps);
+
+
+ /**
+ * Performs a meter operation on the specified vitual device with the
+ * specified parameters.
+ *
+ * @param networkId the identity of the virtual network where this rule applies
+ * @param deviceId device identifier on which the batch of group
+ * operations to be executed
+ * @param meterOp a meter operation
+ */
+ void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperation meterOp);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProviderService.java
new file mode 100644
index 0000000..46e4a7d
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualMeterProviderService.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.provider;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterFeatures;
+import org.onosproject.net.meter.MeterOperation;
+
+import java.util.Collection;
+
+/**
+ * Service through which meter providers can inject information
+ * into the virtual network subsystem core.
+ */
+public interface VirtualMeterProviderService
+ extends VirtualProviderService<VirtualMeterProvider> {
+ /**
+ * Notifies the core that a meter operation failed for a
+ * specific reason.
+ *
+ * @param operation the failed operation
+ * @param reason the failure reason
+ */
+ void meterOperationFailed(MeterOperation operation, MeterFailReason reason);
+
+ /**
+ * Pushes the collection of meters observed on the data plane as
+ * well as their associated statistics.
+ *
+ * @param deviceId a device id
+ * @param meterEntries a collection of meter entries
+ */
+ void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries);
+
+ /**
+ * Pushes the meter features collected from the device.
+ *
+ * @param deviceId the device Id
+ * @param meterfeatures the meter features
+ */
+ void pushMeterFeatures(DeviceId deviceId,
+ MeterFeatures meterfeatures);
+
+ /**
+ * Delete meter features collected from the device.
+ *
+ * @param deviceId the device id
+ */
+ void deleteMeterFeatures(DeviceId deviceId);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProvider.java
new file mode 100644
index 0000000..038d4b3
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProvider.java
@@ -0,0 +1,38 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.provider.Provider;
+
+/**
+ * Entity capable of providing traffic isolation constructs for use in
+ * implementation of virtual devices and virtual links.
+ */
+public interface VirtualNetworkProvider extends Provider {
+
+ /**
+ * Indicates whether or not the specified connect points on the underlying
+ * network are traversable/reachable.
+ *
+ * @param src source connection point
+ * @param dst destination connection point
+ * @return true if the destination is reachable from the source
+ */
+ boolean isTraversable(ConnectPoint src, ConnectPoint dst);
+
+ // TODO: Further enhance this interface to support the virtual intent programming across this boundary.
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderRegistry.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderRegistry.java
new file mode 100644
index 0000000..6fbb1a7
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.provider.ProviderRegistry;
+
+/**
+ * Abstraction of a virtual network provider registry.
+ */
+public interface VirtualNetworkProviderRegistry
+ extends ProviderRegistry<VirtualNetworkProvider, VirtualNetworkProviderService> {
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderService.java
new file mode 100644
index 0000000..ee7d9a6
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualNetworkProviderService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.provider.ProviderService;
+
+import java.util.Set;
+
+/**
+ * Service through which virtual network providers can inject information into
+ * the core.
+ */
+public interface VirtualNetworkProviderService extends ProviderService<VirtualNetworkProvider> {
+
+ /**
+ * Set of separate topology clusters expressed in terms of connect points which
+ * belong to the same SCC of the underlying topology.
+ *
+ * @param clusters set of sets of mutually reachable connection points;
+ * the outer sets are not mutually reachable
+ */
+ void topologyChanged(Set<Set<ConnectPoint>> clusters);
+
+ // TBD: Is the above sufficient to determine health/viability of virtual entities based on
+ // clustering (SCC) of the physical ones?
+
+ /**
+ * This method is used to notify the VirtualNetwork service that a tunnel is now ACTIVE.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @param tunnelId tunnel identifier
+ */
+ void tunnelUp(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId);
+
+ /**
+ * This method is used to notify the VirtualNetwork service that a tunnel is now
+ * FAILED or INACTIVE.
+ *
+ * @param networkId network identifier
+ * @param src source connection point
+ * @param dst destination connection point
+ * @param tunnelId tunnel identifier
+ */
+ void tunnelDown(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId);
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProvider.java
new file mode 100644
index 0000000..4cc8d80
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.packet.OutboundPacket;
+
+/**
+ * Abstraction of a virtual packet provider capable of emitting packets
+ * from virtual network core services to the underlay network.
+ * This provider de-virtualizes and virtualize PacketContext.
+ */
+public interface VirtualPacketProvider extends VirtualProvider {
+ /**
+ * Emits the specified outbound packet onto the underlay physical network.
+ * This provider maps the requested packets for physical network.
+ *
+ * @param networkId the virtual network ID
+ * @param packet outbound packet in the context of virtual network
+ */
+ void emit(NetworkId networkId, OutboundPacket packet);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProviderService.java
new file mode 100644
index 0000000..3c2b4c2
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualPacketProviderService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.packet.PacketContext;
+
+/**
+ * Entity capable of processing inbound packets for virtual networks.
+ */
+public interface VirtualPacketProviderService
+ extends VirtualProviderService<VirtualPacketProvider> {
+
+ /**
+ * Submits inbound packet context for processing to virtual network core.
+ * This processing will be done synchronously, i.e. run-to-completion.
+ * This context is translated with virtual concepts.
+ *
+ * @param context inbound packet context
+ */
+ void processPacket(PacketContext context);
+
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java
new file mode 100644
index 0000000..ac0dfb6
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.provider;
+
+import org.onosproject.net.provider.ProviderId;
+
+/**
+ * Abstraction of a provider of information about virtual network environment.
+ * The role of virtual providers is to translate virtual objects into physical
+ * objects, vice versa.
+ */
+public interface VirtualProvider {
+
+ /**
+ * Returns the provider identifier.
+ *
+ * @return provider identification
+ */
+ ProviderId id();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java
new file mode 100644
index 0000000..546463c
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java
@@ -0,0 +1,122 @@
+/*
+ * 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.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Set;
+
+/**
+ * Registry for tracking information providers with the core.
+ */
+public interface VirtualProviderRegistryService {
+
+ /**
+ * Registers the supplied virtual provider.
+ *
+ * @param virtualProvider a virtual provider to be registered
+ * @throws java.lang.IllegalArgumentException if the provider is registered already
+ */
+ void registerProvider(VirtualProvider virtualProvider);
+
+ /**
+ * Unregisters the supplied virtual provider.
+ * As a result the previously issued virtual provider service will be invalidated
+ * and any subsequent invocations of its methods may throw
+ * {@link java.lang.IllegalStateException}.
+ * <p>
+ * Unregistering a virtual provider that has not been previously registered results
+ * in a no-op.
+ * </p>
+ *
+ * @param virtualProvider a virtual provider to be unregistered
+ */
+ void unregisterProvider(VirtualProvider virtualProvider);
+
+ /**
+ * Registers the supplied virtual provider.
+ *
+ * @param networkId a virtual network identifier
+ * @param virtualProviderService a virtual provider service to be registered
+ */
+ void registerProviderService(NetworkId networkId,
+ VirtualProviderService virtualProviderService);
+
+ /**
+ * Unregisters the supplied virtual provider service.
+ *
+ * @param networkId a virtual network identifier
+ * @param virtualProviderService a virtual provider service to be unregistered
+ */
+ void unregisterProviderService(NetworkId networkId,
+ VirtualProviderService virtualProviderService);
+
+ /**
+ * Returns a set of currently registered virtual provider identities.
+ *
+ * @return set of virtual provider identifiers
+ */
+ Set<ProviderId> getProviders();
+
+ /**
+ * Returns a set of currently registered virtual provider identities
+ * corresponding to the requested providerService.
+ *
+ * @param virtualProviderService a virtual provider service
+ * @return set of virtual provider identifiers
+ */
+ Set<ProviderId> getProvidersByService(VirtualProviderService virtualProviderService);
+
+ /**
+ * Returns the virtual provider registered with the specified provider ID or null
+ * if none is found for the given provider family and default fall-back is
+ * not supported.
+ *
+ * @param providerId provider identifier
+ * @return provider
+ */
+ VirtualProvider getProvider(ProviderId providerId);
+
+ /**
+ * Returns the virtual provider for the specified device ID based on URI scheme.
+ *
+ * @param deviceId virtual device identifier
+ * @return provider bound to the URI scheme
+ */
+ VirtualProvider getProvider(DeviceId deviceId);
+
+ /**
+ * Returns the virtual provider registered with the specified scheme.
+ *
+ * @param scheme provider scheme
+ * @return provider
+ */
+ VirtualProvider getProvider(String scheme);
+
+ /**
+ * Returns a virtual provider service corresponding to
+ * the virtual network and provider class type.
+ *
+ * @param networkId a virtual network identifier
+ * @param providerClass a type of virtual provider
+ * @return a virtual provider service
+ */
+ VirtualProviderService getProviderService(NetworkId networkId,
+ Class<? extends VirtualProvider> providerClass);
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderService.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderService.java
new file mode 100644
index 0000000..8ad378f
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderService.java
@@ -0,0 +1,33 @@
+/*
+ * 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.provider;
+
+/**
+ * Abstraction of a service through which virtual providers can inject information
+ * about the network environment into the virtual core.
+ *
+ * @param <P> type of the information virtual provider
+ */
+
+public interface VirtualProviderService<P extends VirtualProvider> {
+ /**
+ * Returns the virtual provider to which this service has been issued.
+ *
+ * @return virtual provider to which this service has been assigned
+ */
+ P provider();
+}
diff --git a/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/package-info.java b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/package-info.java
new file mode 100644
index 0000000..8d360d5
--- /dev/null
+++ b/apps/virtual/api/src/main/java/org/onosproject/incubator/net/virtual/provider/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.
+ */
+
+/**
+ * Network virtualization provider interfaces and implementation.
+ */
+package org.onosproject.incubator.net.virtual.provider;
\ No newline at end of file
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/ComparatorsTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/ComparatorsTest.java
new file mode 100644
index 0000000..ba208bf
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/ComparatorsTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018-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;
+
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TenantId;
+import org.onosproject.net.provider.ProviderId;
+
+import static org.junit.Assert.*;
+import static org.onosproject.incubator.net.virtual.Comparators.VIRTUAL_DEVICE_COMPARATOR;
+import static org.onosproject.incubator.net.virtual.Comparators.VIRTUAL_NETWORK_COMPARATOR;
+import static org.onosproject.incubator.net.virtual.Comparators.VIRTUAL_PORT_COMPARATOR;
+import static org.onosproject.net.DeviceId.deviceId;
+
+public class ComparatorsTest {
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final DeviceId DID = deviceId("of:foo");
+ private static final String MFR = "whitebox";
+ private static final String HW = "1.1.x";
+ private static final String SW = "3.9.1";
+ private static final String SN = "43311-12345";
+ private static final ChassisId CID = new ChassisId();
+
+ @Test
+ public void testVirtualNetworkComparator() {
+ assertNotEquals(0, VIRTUAL_NETWORK_COMPARATOR.compare(network(10, "tenantID"), network(10, "tenantID1")));
+ assertNotEquals(0, VIRTUAL_NETWORK_COMPARATOR.compare(network(10, "tenantID"), network(15, "tenantID1")));
+ assertNotEquals(0, VIRTUAL_NETWORK_COMPARATOR.compare(network(15, "tenantID1"), network(10, "tenantID1")));
+ assertNotEquals(0, VIRTUAL_NETWORK_COMPARATOR.compare(network(15, "tenantID"), network(10, "tenantID1")));
+ }
+
+ private VirtualNetwork network(int networkID, String tenantID) {
+ return new DefaultVirtualNetwork(NetworkId.networkId(networkID), TenantId.tenantId(tenantID));
+ }
+
+ @Test
+ public void testVirtualDeviceComparator() {
+ assertEquals(0, VIRTUAL_DEVICE_COMPARATOR.compare(vd(0, "of:foo"), vd(0, "of:foo")));
+ assertEquals(0, VIRTUAL_DEVICE_COMPARATOR.compare(vd(3, "of:foo"), vd(0, "of:foo")));
+ assertNotEquals(0, VIRTUAL_DEVICE_COMPARATOR.compare(vd(0, "of:bar"), vd(0, "of:foo")));
+ assertNotEquals(0, VIRTUAL_DEVICE_COMPARATOR.compare(vd(3, "of:bar"), vd(0, "of:foo")));
+ }
+
+ private VirtualDevice vd(int netID, String devID) {
+ return new DefaultVirtualDevice(NetworkId.networkId(netID), DeviceId.deviceId(devID));
+ }
+
+ @Test
+ public void testVirtualPortComparator() {
+ assertEquals(0, VIRTUAL_PORT_COMPARATOR.compare(vPort(2), vPort(2)));
+ assertEquals(4, VIRTUAL_PORT_COMPARATOR.compare(vPort(900), vPort(5)));
+ assertEquals(-8, VIRTUAL_PORT_COMPARATOR.compare(vPort(0), vPort(8)));
+ }
+
+ private VirtualPort vPort(int portNumber) {
+ return new DefaultVirtualPort(NetworkId.networkId(20),
+ new DefaultDevice(PID, DID, null, MFR, HW, SW, SN, CID),
+ PortNumber.portNumber(portNumber),
+ new ConnectPoint(DID, PortNumber.portNumber(900)));
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java
new file mode 100644
index 0000000..22be664
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.TestDeviceParams;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual device model entity.
+ */
+public class DefaultVirtualDeviceTest extends TestDeviceParams {
+
+ /**
+ * Checks that the DefaultVirtualDevice class is immutable.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultVirtualDevice.class);
+ }
+
+ @Test
+ public void testEquality() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device3 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+ DefaultVirtualDevice device4 =
+ new DefaultVirtualDevice(NetworkId.networkId(1), DID1);
+
+ new EqualsTester().addEqualityGroup(device1, device2).addEqualityGroup(device3)
+ .addEqualityGroup(device4).testEquals();
+ }
+}
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualHostTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualHostTest.java
new file mode 100644
index 0000000..7fcf70d
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualHostTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.TestDeviceParams;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual host model entity.
+ */
+public class DefaultVirtualHostTest extends TestDeviceParams {
+
+ /**
+ * Checks that the DefaultVirtualHost class is immutable.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultVirtualHost.class);
+ }
+
+ /**
+ * Tests the DefaultVirtualHost equality method.
+ */
+ @Test
+ public void testEquality() {
+ DefaultVirtualHost host1 =
+ new DefaultVirtualHost(NetworkId.networkId(0), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ DefaultVirtualHost host2 =
+ new DefaultVirtualHost(NetworkId.networkId(0), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ DefaultVirtualHost host3 =
+ new DefaultVirtualHost(NetworkId.networkId(0), HID2, MAC1, VLAN1, LOC1, IPSET1);
+ DefaultVirtualHost host4 =
+ new DefaultVirtualHost(NetworkId.networkId(1), HID2, MAC1, VLAN1, LOC1, IPSET1);
+
+ new EqualsTester().addEqualityGroup(host1, host2).addEqualityGroup(host3)
+ .addEqualityGroup(host4).testEquals();
+ }
+}
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java
new file mode 100644
index 0000000..b96290f
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual link model entity.
+ */
+public class DefaultVirtualLinkTest extends TestDeviceParams {
+
+ /**
+ * Checks that the DefaultVirtualLink class is immutable.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultVirtualLink.class);
+ }
+
+ /**
+ * Tests the DefaultVirtualLink Builder to ensure that the src cannot be null.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testBuilderNullSrc() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+ ConnectPoint src = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+ DefaultVirtualLink.builder()
+ .src(null)
+ .build();
+ }
+
+ /**
+ * Tests the DefaultVirtualLink Builder to ensure that the dst cannot be null.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testBuilderNullDst() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+ ConnectPoint src = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+ DefaultVirtualLink.builder()
+ .dst(null)
+ .build();
+ }
+
+ /**
+ * Tests the DefaultVirtualLink Builder to ensure that the networkId cannot be null.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testBuilderNullNetworkId() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+ ConnectPoint src = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+ DefaultVirtualLink.builder()
+ .networkId(null)
+ .build();
+ }
+
+ /**
+ * Tests the DefaultVirtualLink equality method.
+ */
+ @Test
+ public void testEquality() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+ ConnectPoint src = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+ VirtualLink link1 = DefaultVirtualLink.builder()
+ .networkId(NetworkId.networkId(0))
+ .src(src)
+ .dst(dst)
+ .tunnelId(TunnelId.valueOf("1"))
+ .build();
+ VirtualLink link2 = DefaultVirtualLink.builder()
+ .networkId(NetworkId.networkId(0))
+ .src(src)
+ .dst(dst)
+ .tunnelId(TunnelId.valueOf("1"))
+ .build();
+ VirtualLink link3 = DefaultVirtualLink.builder()
+ .networkId(NetworkId.networkId(0))
+ .src(src)
+ .dst(dst)
+ .tunnelId(TunnelId.valueOf("2"))
+ .build();
+ VirtualLink link4 = DefaultVirtualLink.builder()
+ .networkId(NetworkId.networkId(1))
+ .src(src)
+ .dst(dst)
+ .tunnelId(TunnelId.valueOf("3"))
+ .build();
+
+ new EqualsTester().addEqualityGroup(link1, link2).addEqualityGroup(link3)
+ .addEqualityGroup(link4).testEquals();
+ }
+}
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.java
new file mode 100644
index 0000000..7ff4715
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.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;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.TenantId;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual network model entity.
+ */
+public class DefaultVirtualNetworkTest {
+ final String tenantIdValue1 = "TENANT_ID1";
+ final String tenantIdValue2 = "TENANT_ID2";
+
+ /**
+ * Checks that the DefaultVirtualNetwork class is immutable.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultVirtualNetwork.class);
+ }
+
+ @Test
+ public void testEquality() {
+ DefaultVirtualNetwork network1 =
+ new DefaultVirtualNetwork(NetworkId.networkId(0), TenantId.tenantId(tenantIdValue1));
+ DefaultVirtualNetwork network2 =
+ new DefaultVirtualNetwork(NetworkId.networkId(0), TenantId.tenantId(tenantIdValue1));
+ DefaultVirtualNetwork network3 =
+ new DefaultVirtualNetwork(NetworkId.networkId(0), TenantId.tenantId(tenantIdValue2));
+ DefaultVirtualNetwork network4 =
+ new DefaultVirtualNetwork(NetworkId.networkId(1), TenantId.tenantId(tenantIdValue2));
+
+ new EqualsTester().addEqualityGroup(network1, network2).addEqualityGroup(network3)
+ .addEqualityGroup(network4).testEquals();
+ }
+}
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java
new file mode 100644
index 0000000..61ba611
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual port model entity.
+ */
+public class DefaultVirtualPortTest extends TestDeviceParams {
+ /**
+ * Checks that the DefaultVirtualPort class is immutable.
+ */
+ @Test
+ public void testImmutability() {
+ assertThatClassIsImmutable(DefaultVirtualPort.class);
+ }
+
+ @Test
+ public void testEquality() {
+ DefaultVirtualDevice device1 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID1);
+ DefaultVirtualDevice device2 =
+ new DefaultVirtualDevice(NetworkId.networkId(0), DID2);
+
+ ConnectPoint cpA = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+ ConnectPoint cpB = new ConnectPoint(device1.id(), PortNumber.portNumber(2));
+ ConnectPoint cpC = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+ DefaultVirtualPort port1 =
+ new DefaultVirtualPort(NetworkId.networkId(0), device1,
+ PortNumber.portNumber(1), cpA);
+ DefaultVirtualPort port2 =
+ new DefaultVirtualPort(NetworkId.networkId(0), device1,
+ PortNumber.portNumber(1), cpA);
+ DefaultVirtualPort port3 =
+ new DefaultVirtualPort(NetworkId.networkId(0), device1,
+ PortNumber.portNumber(2), cpB);
+ DefaultVirtualPort port4 =
+ new DefaultVirtualPort(NetworkId.networkId(1), device2,
+ PortNumber.portNumber(2), cpC);
+
+
+ new EqualsTester().addEqualityGroup(port1, port2).addEqualityGroup(port3)
+ .addEqualityGroup(port4).testEquals();
+ }
+}
diff --git a/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodecTest.java b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodecTest.java
new file mode 100644
index 0000000..043e434
--- /dev/null
+++ b/apps/virtual/api/src/test/java/org/onosproject/incubator/net/virtual/codec/VirtualHostCodecTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018-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.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.incubator.net.virtual.DefaultVirtualHost;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.NetTestTools;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isOneOf;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Tests VirtualHostCodec class.
+ */
+@Ignore
+public class VirtualHostCodecTest {
+
+ private static final String TEST_IP1 = "1.1.1.1";
+ private static final String TEST_IP2 = "2.2.2.2";
+ private static final String TEST_HOST_ID = "12:34:56:78:90:11/1";
+ private static final String TEST_MAC_ADDRESS = "11:11:22:22:33:33";
+ private static final long TEST_NETWORK_ID = 44L;
+ private static final short TEST_VLAN_ID = (short) 12;
+ private static final ConnectPoint CONNECT_POINT =
+ NetTestTools.connectPoint("d1", 1);
+
+ @Test
+ public void testEncode() {
+ MockCodecContext context = new MockCodecContext();
+ NetworkId networkId = NetworkId.networkId(TEST_NETWORK_ID);
+ HostId id = NetTestTools.hid(TEST_HOST_ID);
+ MacAddress mac = MacAddress.valueOf(TEST_MAC_ADDRESS);
+ VlanId vlan = VlanId.vlanId(TEST_VLAN_ID);
+ HostLocation location =
+ new HostLocation(CONNECT_POINT, 0L);
+ Set<IpAddress> ips = ImmutableSet.of(IpAddress.valueOf(TEST_IP1),
+ IpAddress.valueOf(TEST_IP2));
+ VirtualHost host =
+ new DefaultVirtualHost(networkId, id, mac, vlan, location, ips);
+ JsonCodec<VirtualHost> codec = context.codec(VirtualHost.class);
+ ObjectNode node = codec.encode(host, context);
+
+ assertThat(node.get(VirtualHostCodec.NETWORK_ID).asLong(),
+ is(TEST_NETWORK_ID));
+ assertThat(node.get(VirtualHostCodec.HOST_ID).asText(),
+ is(TEST_HOST_ID));
+ assertThat(node.get(VirtualHostCodec.MAC_ADDRESS).asText(),
+ is(TEST_MAC_ADDRESS));
+ assertThat(node.get(VirtualHostCodec.VLAN).asInt(),
+ is((int) TEST_VLAN_ID));
+ assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get(0).get("elementId").asText(),
+ is(location.deviceId().toString()));
+ assertThat(node.get(VirtualHostCodec.HOST_LOCATION).get(0).get("port").asLong(),
+ is(location.port().toLong()));
+
+ JsonNode jsonIps = node.get(VirtualHostCodec.IP_ADDRESSES);
+ assertThat(jsonIps, notNullValue());
+ assertThat(jsonIps.isArray(), is(true));
+ assertThat(jsonIps.size(), is(ips.size()));
+
+ IntStream.of(0, 1).forEach(index ->
+ assertThat(jsonIps.get(index).asText(),
+ isOneOf(TEST_IP1, TEST_IP2)));
+ }
+
+ @Test
+ public void testDecode() throws IOException {
+ MockCodecContext context = new MockCodecContext();
+ InputStream jsonStream =
+ VirtualHostCodecTest.class.getResourceAsStream("VirtualHost.json");
+ JsonNode json = context.mapper().readTree(jsonStream);
+ assertThat(json, notNullValue());
+ JsonCodec<VirtualHost> codec = context.codec(VirtualHost.class);
+ VirtualHost virtualHost = codec.decode((ObjectNode) json, context);
+ assertThat(virtualHost, notNullValue());
+
+ assertThat(virtualHost.networkId().id(),
+ is(TEST_NETWORK_ID));
+ assertThat(virtualHost.id().toString(),
+ is(NetTestTools.hid(TEST_MAC_ADDRESS + "/12").toString()));
+ assertThat(virtualHost.mac().toString(),
+ is(TEST_MAC_ADDRESS));
+ assertThat(virtualHost.vlan().id(),
+ is((short) TEST_VLAN_ID));
+ assertThat(virtualHost.location().deviceId(),
+ is(CONNECT_POINT.deviceId()));
+ assertThat(virtualHost.location().port().toLong(),
+ is(CONNECT_POINT.port().toLong()));
+
+
+ assertThat(virtualHost.ipAddresses().contains(IpAddress.valueOf(TEST_IP1)),
+ is(true));
+ assertThat(virtualHost.ipAddresses().contains(IpAddress.valueOf(TEST_IP2)),
+ is(true));
+ }
+}
diff --git a/apps/virtual/api/src/test/resources/org/onosproject/incubator/net/virtual/codec/VirtualHost.json b/apps/virtual/api/src/test/resources/org/onosproject/incubator/net/virtual/codec/VirtualHost.json
new file mode 100644
index 0000000..818ce0b
--- /dev/null
+++ b/apps/virtual/api/src/test/resources/org/onosproject/incubator/net/virtual/codec/VirtualHost.json
@@ -0,0 +1,15 @@
+{
+ "networkId": "44",
+ "mac": "11:11:22:22:33:33",
+ "vlan": "12",
+ "ipAddresses": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ],
+ "locations": [
+ {
+ "elementId": "of:d1",
+ "port": "1"
+ }
+ ]
+}
diff --git a/apps/virtual/app/BUILD b/apps/virtual/app/BUILD
new file mode 100644
index 0000000..15941f8
--- /dev/null
+++ b/apps/virtual/app/BUILD
@@ -0,0 +1,26 @@
+COMPILE_DEPS = CORE_DEPS + JACKSON + REST + CLI + [
+ "@metrics_core//jar",
+ "//core/common:onos-core-common",
+ "//core/net:onos-core-net",
+ "//core/store/serializers:onos-core-serializers",
+ "//apps/tunnel/api:onos-apps-tunnel-api",
+ "//apps/virtual/api:onos-apps-virtual-api",
+]
+
+TEST_DEPS = TEST_ADAPTERS + [
+ "@minimal_json//jar",
+ "@jersey_client//jar",
+ "//web/api:onos-rest-tests",
+ "//utils/osgi:onlab-osgi-tests",
+]
+
+osgi_jar_with_tests(
+ api_description = "REST API for Virtual Networks",
+ api_package = "org.onosproject.incubator.net.virtual.rest",
+ api_title = "Virtual Networks",
+ api_version = "1.0",
+ karaf_command_packages = ["org.onosproject.incubator.net.virtual.cli"],
+ test_deps = TEST_DEPS,
+ visibility = ["//visibility:public"],
+ deps = COMPILE_DEPS,
+)
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
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManager.java
new file mode 100644
index 0000000..21ba3b9
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManager.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceEvent.Type;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortStatistics;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Device service implementation built on the virtual network service.
+ */
+public class VirtualNetworkDeviceManager
+ extends AbstractVirtualListenerManager<DeviceEvent, DeviceListener>
+ implements DeviceService {
+
+ private static final String TYPE_NULL = "Type cannot be null";
+ private static final String DEVICE_NULL = "Device cannot be null";
+ private static final String PORT_NUMBER_NULL = "PortNumber cannot be null";
+ private VirtualNetworkListener virtualNetworkListener = new InternalVirtualNetworkListener();
+
+ /**
+ * Creates a new VirtualNetworkDeviceService object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkDeviceManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId, DeviceEvent.class);
+ manager.addListener(virtualNetworkListener);
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return manager.getVirtualDevices(this.networkId).size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return manager.getVirtualDevices(
+ this.networkId).stream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Iterable<Device> getDevices(Device.Type type) {
+ checkNotNull(type, TYPE_NULL);
+ return manager.getVirtualDevices(this.networkId)
+ .stream()
+ .filter(device -> type.equals(device.type()))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices() {
+ return getDevices();
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices(Device.Type type) {
+ return getDevices(type);
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ Optional<VirtualDevice> foundDevice =
+ manager.getVirtualDevices(this.networkId)
+ .stream()
+ .filter(device -> deviceId.equals(device.id()))
+ .findFirst();
+ if (foundDevice.isPresent()) {
+ return foundDevice.get();
+ }
+ return null;
+ }
+
+ @Override
+ public MastershipRole getRole(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ // TODO hard coded to master for now.
+ return MastershipRole.MASTER;
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ return manager.getVirtualPorts(this.networkId, deviceId)
+ .stream()
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ // TODO not supported at the moment.
+ return ImmutableList.of();
+ }
+
+ @Override
+ public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ // TODO not supported at the moment.
+ return ImmutableList.of();
+ }
+
+ @Override
+ public PortStatistics getStatisticsForPort(DeviceId deviceId,
+ PortNumber portNumber) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(deviceId, PORT_NUMBER_NULL);
+ // TODO not supported at the moment.
+ return null;
+ }
+
+ @Override
+ public PortStatistics getDeltaStatisticsForPort(DeviceId deviceId,
+ PortNumber portNumber) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(deviceId, PORT_NUMBER_NULL);
+ // TODO not supported at the moment.
+ return null;
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ checkNotNull(deviceId, DEVICE_NULL);
+
+ Optional<VirtualPort> foundPort =
+ manager.getVirtualPorts(this.networkId, deviceId)
+ .stream()
+ .filter(port -> port.number().equals(portNumber))
+ .findFirst();
+ if (foundPort.isPresent()) {
+ return foundPort.get();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return getDevice(deviceId) != null;
+ }
+
+ @Override
+ public String localStatus(DeviceId deviceId) {
+ // TODO not supported at this time
+ return null;
+ }
+
+ @Override
+ public long getLastUpdatedInstant(DeviceId deviceId) {
+ // TODO not supported at this time
+ return 0;
+ }
+
+ /**
+ * Translates VirtualNetworkEvent to DeviceEvent.
+ */
+ private class InternalVirtualNetworkListener implements VirtualNetworkListener {
+ @Override
+ public boolean isRelevant(VirtualNetworkEvent event) {
+ return networkId().equals(event.subject());
+ }
+
+ @Override
+ public void event(VirtualNetworkEvent event) {
+ switch (event.type()) {
+ case VIRTUAL_DEVICE_ADDED:
+ post(new DeviceEvent(Type.DEVICE_ADDED, event.virtualDevice()));
+ break;
+ case VIRTUAL_DEVICE_UPDATED:
+ post(new DeviceEvent(Type.DEVICE_UPDATED, event.virtualDevice()));
+ break;
+ case VIRTUAL_DEVICE_REMOVED:
+ post(new DeviceEvent(Type.DEVICE_REMOVED, event.virtualDevice()));
+ break;
+ case VIRTUAL_PORT_ADDED:
+ post(new DeviceEvent(Type.PORT_ADDED, event.virtualDevice(), event.virtualPort()));
+ break;
+ case VIRTUAL_PORT_UPDATED:
+ post(new DeviceEvent(Type.PORT_UPDATED, event.virtualDevice(), event.virtualPort()));
+ break;
+ case VIRTUAL_PORT_REMOVED:
+ post(new DeviceEvent(Type.PORT_REMOVED, event.virtualDevice(), event.virtualPort()));
+ break;
+ case NETWORK_UPDATED:
+ case NETWORK_REMOVED:
+ case NETWORK_ADDED:
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
new file mode 100644
index 0000000..c4e2031
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalCause;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.incubator.net.virtual.AbstractVnetService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupKey;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of the flow objective programming service for virtual networks.
+ */
+// NOTE: This manager is designed to provide flow objective programming service
+// for virtual networks. Actually, virtual networks don't need to consider
+// the different implementation of data-path pipeline. But, the interfaces
+// and usages of flow objective service are still valuable for virtual network.
+// This manager is working as an interpreter from FlowObjective to FlowRules
+// to provide symmetric interfaces with ONOS core services.
+// The behaviours are based on DefaultSingleTablePipeline.
+
+public class VirtualNetworkFlowObjectiveManager extends AbstractVnetService
+ implements FlowObjectiveService {
+
+ public static final int INSTALL_RETRY_ATTEMPTS = 5;
+ public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
+
+ private final Logger log = getLogger(getClass());
+
+ protected DeviceService deviceService;
+
+ // Note: The following dependencies are added on behalf of the pipeline
+ // driver behaviours to assure these services are available for their
+ // initialization.
+ protected FlowRuleService flowRuleService;
+
+ protected VirtualNetworkFlowObjectiveStore virtualFlowObjectiveStore;
+ protected FlowObjectiveStore flowObjectiveStore;
+ private final FlowObjectiveStoreDelegate delegate;
+
+ private final PipelinerContext context = new InnerPipelineContext();
+
+ private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
+
+ // local stores for queuing fwd and next objectives that are waiting for an
+ // associated next objective execution to complete. The signal for completed
+ // execution comes from a pipeline driver, in this or another controller
+ // instance, via the DistributedFlowObjectiveStore.
+ private final Map<Integer, Set<PendingFlowObjective>> pendingForwards =
+ Maps.newConcurrentMap();
+ private final Map<Integer, Set<PendingFlowObjective>> pendingNexts =
+ Maps.newConcurrentMap();
+
+ // local store to track which nextObjectives were sent to which device
+ // for debugging purposes
+ private Map<Integer, DeviceId> nextToDevice = Maps.newConcurrentMap();
+
+ private ExecutorService executorService;
+
+ public VirtualNetworkFlowObjectiveManager(VirtualNetworkService manager,
+ NetworkId networkId) {
+ super(manager, networkId);
+
+ deviceService = manager.get(networkId(), DeviceService.class);
+ flowRuleService = manager.get(networkId(), FlowRuleService.class);
+
+ executorService = newFixedThreadPool(4, groupedThreads("onos/virtual/objective-installer", "%d", log));
+
+ virtualFlowObjectiveStore =
+ serviceDirectory.get(VirtualNetworkFlowObjectiveStore.class);
+ delegate = new InternalStoreDelegate();
+ virtualFlowObjectiveStore.setDelegate(networkId(), delegate);
+ flowObjectiveStore = new StoreConvertor();
+ }
+
+ @Override
+ public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
+ executorService.execute(new ObjectiveInstaller(deviceId, filteringObjective));
+ }
+
+ @Override
+ public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ if (forwardingObjective.nextId() == null ||
+ forwardingObjective.op() == Objective.Operation.REMOVE ||
+ flowObjectiveStore.getNextGroup(forwardingObjective.nextId()) != null ||
+ !queueFwdObjective(deviceId, forwardingObjective)) {
+ // fast path
+ executorService.execute(new ObjectiveInstaller(deviceId, forwardingObjective));
+ }
+ }
+
+ @Override
+ public void next(DeviceId deviceId, NextObjective nextObjective) {
+ nextToDevice.put(nextObjective.id(), deviceId);
+ if (nextObjective.op() == Objective.Operation.ADD ||
+ flowObjectiveStore.getNextGroup(nextObjective.id()) != null ||
+ !queueNextObjective(deviceId, nextObjective)) {
+ // either group exists or we are trying to create it - let it through
+ executorService.execute(new ObjectiveInstaller(deviceId, nextObjective));
+ }
+ }
+
+ @Override
+ public int allocateNextId() {
+ return flowObjectiveStore.allocateNextId();
+ }
+
+ @Override
+ public void initPolicy(String policy) {
+
+ }
+
+ @Override
+ public List<String> getNextMappings() {
+ List<String> mappings = new ArrayList<>();
+ Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
+ // XXX if the NextGroup after de-serialization actually stored info of the deviceId
+ // then info on any nextObj could be retrieved from one controller instance.
+ // Right now the drivers on one instance can only fetch for next-ids that came
+ // to them.
+ // Also, we still need to send the right next-id to the right driver as potentially
+ // there can be different drivers for different devices. But on that account,
+ // no instance should be decoding for another instance's nextIds.
+
+ for (Map.Entry<Integer, NextGroup> e : allnexts.entrySet()) {
+ // get the device this next Objective was sent to
+ DeviceId deviceId = nextToDevice.get(e.getKey());
+ mappings.add("NextId " + e.getKey() + ": " +
+ ((deviceId != null) ? deviceId : "nextId not in this onos instance"));
+ if (deviceId != null) {
+ // this instance of the controller sent the nextObj to a driver
+ Pipeliner pipeliner = getDevicePipeliner(deviceId);
+ List<String> nextMappings = pipeliner.getNextMappings(e.getValue());
+ if (nextMappings != null) {
+ mappings.addAll(nextMappings);
+ }
+ }
+ }
+ return mappings;
+ }
+
+ @Override
+ public List<String> getPendingFlowObjectives() {
+ List<String> pendingFlowObjectives = new ArrayList<>();
+
+ for (Integer nextId : pendingForwards.keySet()) {
+ Set<PendingFlowObjective> pfwd = pendingForwards.get(nextId);
+ StringBuilder pend = new StringBuilder();
+ pend.append("NextId: ")
+ .append(nextId);
+ for (PendingFlowObjective pf : pfwd) {
+ pend.append("\n FwdId: ")
+ .append(String.format("%11s", pf.flowObjective().id()))
+ .append(", DeviceId: ")
+ .append(pf.deviceId())
+ .append(", Selector: ")
+ .append(((ForwardingObjective) pf.flowObjective())
+ .selector().criteria());
+ }
+ pendingFlowObjectives.add(pend.toString());
+ }
+
+ for (Integer nextId : pendingNexts.keySet()) {
+ Set<PendingFlowObjective> pnext = pendingNexts.get(nextId);
+ StringBuilder pend = new StringBuilder();
+ pend.append("NextId: ")
+ .append(nextId);
+ for (PendingFlowObjective pn : pnext) {
+ pend.append("\n NextOp: ")
+ .append(pn.flowObjective().op())
+ .append(", DeviceId: ")
+ .append(pn.deviceId())
+ .append(", Treatments: ")
+ .append(((NextObjective) pn.flowObjective())
+ .next());
+ }
+ pendingFlowObjectives.add(pend.toString());
+ }
+
+ return pendingFlowObjectives;
+ }
+
+ private boolean queueFwdObjective(DeviceId deviceId, ForwardingObjective fwd) {
+ boolean queued = false;
+ synchronized (pendingForwards) {
+ // double check the flow objective store, because this block could run
+ // after a notification arrives
+ if (flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
+ pendingForwards.compute(fwd.nextId(), (id, pending) -> {
+ PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, fwd);
+ if (pending == null) {
+ return Sets.newHashSet(pendfo);
+ } else {
+ pending.add(pendfo);
+ return pending;
+ }
+ });
+ queued = true;
+ }
+ }
+ if (queued) {
+ log.debug("Queued forwarding objective {} for nextId {} meant for device {}",
+ fwd.id(), fwd.nextId(), deviceId);
+ }
+ return queued;
+ }
+
+ private boolean queueNextObjective(DeviceId deviceId, NextObjective next) {
+
+ // we need to hold off on other operations till we get notified that the
+ // initial group creation has succeeded
+ boolean queued = false;
+ synchronized (pendingNexts) {
+ // double check the flow objective store, because this block could run
+ // after a notification arrives
+ if (flowObjectiveStore.getNextGroup(next.id()) == null) {
+ pendingNexts.compute(next.id(), (id, pending) -> {
+ PendingFlowObjective pendfo = new PendingFlowObjective(deviceId, next);
+ if (pending == null) {
+ return Sets.newHashSet(pendfo);
+ } else {
+ pending.add(pendfo);
+ return pending;
+ }
+ });
+ queued = true;
+ }
+ }
+ if (queued) {
+ log.debug("Queued next objective {} with operation {} meant for device {}",
+ next.id(), next.op(), deviceId);
+ }
+ return queued;
+ }
+
+ /**
+ * Task that passes the flow objective down to the driver. The task will
+ * make a few attempts to find the appropriate driver, then eventually give
+ * up and report an error if no suitable driver could be found.
+ */
+ private class ObjectiveInstaller implements Runnable {
+ private final DeviceId deviceId;
+ private final Objective objective;
+
+ private final int numAttempts;
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
+ this(deviceId, objective, 1);
+ }
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
+ this.deviceId = checkNotNull(deviceId);
+ this.objective = checkNotNull(objective);
+ this.numAttempts = attemps;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Pipeliner pipeliner = getDevicePipeliner(deviceId);
+
+ if (pipeliner != null) {
+ if (objective instanceof NextObjective) {
+ nextToDevice.put(objective.id(), deviceId);
+ pipeliner.next((NextObjective) objective);
+ } else if (objective instanceof ForwardingObjective) {
+ pipeliner.forward((ForwardingObjective) objective);
+ } else {
+ pipeliner.filter((FilteringObjective) objective);
+ }
+ //Attempts to check if pipeliner is null for retry attempts
+ } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
+ Thread.sleep(INSTALL_RETRY_INTERVAL);
+ executorService.execute(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
+ } else {
+ // Otherwise we've tried a few times and failed, report an
+ // error back to the user.
+ objective.context().ifPresent(
+ c -> c.onError(objective, ObjectiveError.NOPIPELINER));
+ }
+ //Exception thrown
+ } catch (Exception e) {
+ log.warn("Exception while installing flow objective", e);
+ }
+ }
+ }
+
+ private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
+ @Override
+ public void notify(ObjectiveEvent event) {
+ if (event.type() == ObjectiveEvent.Type.ADD) {
+ log.debug("Received notification of obj event {}", event);
+ Set<PendingFlowObjective> pending;
+
+ // first send all pending flows
+ synchronized (pendingForwards) {
+ // needs to be synchronized for queueObjective lookup
+ pending = pendingForwards.remove(event.subject());
+ }
+ if (pending == null) {
+ log.debug("No forwarding objectives pending for this "
+ + "obj event {}", event);
+ } else {
+ log.debug("Processing {} pending forwarding objectives for nextId {}",
+ pending.size(), event.subject());
+ pending.forEach(p -> getDevicePipeliner(p.deviceId())
+ .forward((ForwardingObjective) p.flowObjective()));
+ }
+
+ // now check for pending next-objectives
+ synchronized (pendingNexts) {
+ // needs to be synchronized for queueObjective lookup
+ pending = pendingNexts.remove(event.subject());
+ }
+ if (pending == null) {
+ log.debug("No next objectives pending for this "
+ + "obj event {}", event);
+ } else {
+ log.debug("Processing {} pending next objectives for nextId {}",
+ pending.size(), event.subject());
+ pending.forEach(p -> getDevicePipeliner(p.deviceId())
+ .next((NextObjective) p.flowObjective()));
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves (if it exists) the device pipeline behaviour from the cache.
+ * Otherwise it warms the caches and triggers the init method of the Pipeline.
+ * For virtual network, it returns OVS pipeliner.
+ *
+ * @param deviceId the id of the device associated to the pipeline
+ * @return the implementation of the Pipeliner behaviour
+ */
+ private Pipeliner getDevicePipeliner(DeviceId deviceId) {
+ return pipeliners.computeIfAbsent(deviceId, this::initPipelineHandler);
+ }
+
+ /**
+ * Creates and initialize {@link Pipeliner}.
+ * <p>
+ * Note: Expected to be called under per-Device lock.
+ * e.g., {@code pipeliners}' Map#compute family methods
+ *
+ * @param deviceId Device to initialize pipeliner
+ * @return {@link Pipeliner} instance or null
+ */
+ private Pipeliner initPipelineHandler(DeviceId deviceId) {
+ //FIXME: do we need a standard pipeline for virtual device?
+ Pipeliner pipeliner = new DefaultVirtualDevicePipeline();
+ pipeliner.init(deviceId, context);
+ return pipeliner;
+ }
+
+ // Processing context for initializing pipeline driver behaviours.
+ private class InnerPipelineContext implements PipelinerContext {
+ @Override
+ public ServiceDirectory directory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public FlowObjectiveStore store() {
+ return flowObjectiveStore;
+ }
+ }
+
+ /**
+ * Data class used to hold a pending flow objective that could not
+ * be processed because the associated next object was not present.
+ * Note that this pending flow objective could be a forwarding objective
+ * waiting for a next objective to complete execution. Or it could a
+ * next objective (with a different operation - remove, addToExisting, or
+ * removeFromExisting) waiting for a next objective with the same id to
+ * complete execution.
+ */
+ private class PendingFlowObjective {
+ private final DeviceId deviceId;
+ private final Objective flowObj;
+
+ public PendingFlowObjective(DeviceId deviceId, Objective flowObj) {
+ this.deviceId = deviceId;
+ this.flowObj = flowObj;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public Objective flowObjective() {
+ return flowObj;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(deviceId, flowObj);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PendingFlowObjective)) {
+ return false;
+ }
+ final PendingFlowObjective other = (PendingFlowObjective) obj;
+ if (this.deviceId.equals(other.deviceId) &&
+ this.flowObj.equals(other.flowObj)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * This class is a wrapping class from VirtualNetworkFlowObjectiveStore
+ * to FlowObjectiveStore for PipelinerContext.
+ */
+ private class StoreConvertor implements FlowObjectiveStore {
+
+ @Override
+ public void setDelegate(FlowObjectiveStoreDelegate delegate) {
+ virtualFlowObjectiveStore.setDelegate(networkId(), delegate);
+ }
+
+ @Override
+ public void unsetDelegate(FlowObjectiveStoreDelegate delegate) {
+ virtualFlowObjectiveStore.unsetDelegate(networkId(), delegate);
+ }
+
+ @Override
+ public boolean hasDelegate() {
+ return virtualFlowObjectiveStore.hasDelegate(networkId());
+ }
+
+ @Override
+ public void putNextGroup(Integer nextId, NextGroup group) {
+ virtualFlowObjectiveStore.putNextGroup(networkId(), nextId, group);
+ }
+
+ @Override
+ public NextGroup getNextGroup(Integer nextId) {
+ return virtualFlowObjectiveStore.getNextGroup(networkId(), nextId);
+ }
+
+ @Override
+ public NextGroup removeNextGroup(Integer nextId) {
+ return virtualFlowObjectiveStore.removeNextGroup(networkId(), nextId);
+ }
+
+ @Override
+ public Map<Integer, NextGroup> getAllGroups() {
+ return virtualFlowObjectiveStore.getAllGroups(networkId());
+ }
+
+ @Override
+ public int allocateNextId() {
+ return virtualFlowObjectiveStore.allocateNextId(networkId());
+ }
+ }
+
+ /**
+ * Simple single table pipeline abstraction for virtual networks.
+ */
+ private class DefaultVirtualDevicePipeline
+ extends AbstractHandlerBehaviour implements Pipeliner {
+
+ private final Logger log = getLogger(getClass());
+
+ private DeviceId deviceId;
+
+ private Cache<Integer, NextObjective> pendingNext;
+
+ private KryoNamespace appKryo = new KryoNamespace.Builder()
+ .register(GroupKey.class)
+ .register(DefaultGroupKey.class)
+ .register(SingleGroup.class)
+ .register(byte[].class)
+ .build("DefaultVirtualDevicePipeline");
+
+ @Override
+ public void init(DeviceId deviceId, PipelinerContext context) {
+ this.deviceId = deviceId;
+
+ pendingNext = CacheBuilder.newBuilder()
+ .expireAfterWrite(20, TimeUnit.SECONDS)
+ .removalListener((RemovalNotification<Integer, NextObjective> notification) -> {
+ if (notification.getCause() == RemovalCause.EXPIRED) {
+ notification.getValue().context()
+ .ifPresent(c -> c.onError(notification.getValue(),
+ ObjectiveError.FLOWINSTALLATIONFAILED));
+ }
+ }).build();
+ }
+
+ @Override
+ public void filter(FilteringObjective filter) {
+
+ TrafficTreatment.Builder actions;
+ switch (filter.type()) {
+ case PERMIT:
+ actions = (filter.meta() == null) ?
+ DefaultTrafficTreatment.builder().punt() :
+ DefaultTrafficTreatment.builder(filter.meta());
+ break;
+ case DENY:
+ actions = (filter.meta() == null) ?
+ DefaultTrafficTreatment.builder() :
+ DefaultTrafficTreatment.builder(filter.meta());
+ actions.drop();
+ break;
+ default:
+ log.warn("Unknown filter type: {}", filter.type());
+ actions = DefaultTrafficTreatment.builder().drop();
+ }
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+ filter.conditions().forEach(selector::add);
+
+ if (filter.key() != null) {
+ selector.add(filter.key());
+ }
+
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector.build())
+ .withTreatment(actions.build())
+ .fromApp(filter.appId())
+ .withPriority(filter.priority());
+
+ if (filter.permanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(filter.timeout());
+ }
+
+ installObjective(ruleBuilder, filter);
+ }
+
+ @Override
+ public void forward(ForwardingObjective fwd) {
+ TrafficSelector selector = fwd.selector();
+
+ if (fwd.treatment() != null) {
+ // Deal with SPECIFIC and VERSATILE in the same manner.
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector)
+ .fromApp(fwd.appId())
+ .withPriority(fwd.priority())
+ .withTreatment(fwd.treatment());
+
+ if (fwd.permanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(fwd.timeout());
+ }
+ installObjective(ruleBuilder, fwd);
+
+ } else {
+ NextObjective nextObjective = pendingNext.getIfPresent(fwd.nextId());
+ if (nextObjective != null) {
+ pendingNext.invalidate(fwd.nextId());
+ nextObjective.next().forEach(treat -> {
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector)
+ .fromApp(fwd.appId())
+ .withPriority(fwd.priority())
+ .withTreatment(treat);
+
+ if (fwd.permanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(fwd.timeout());
+ }
+ installObjective(ruleBuilder, fwd);
+ });
+ } else {
+ fwd.context().ifPresent(c -> c.onError(fwd,
+ ObjectiveError.GROUPMISSING));
+ }
+ }
+ }
+
+ private void installObjective(FlowRule.Builder ruleBuilder, Objective objective) {
+ FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
+ switch (objective.op()) {
+
+ case ADD:
+ flowBuilder.add(ruleBuilder.build());
+ break;
+ case REMOVE:
+ flowBuilder.remove(ruleBuilder.build());
+ break;
+ default:
+ log.warn("Unknown operation {}", objective.op());
+ }
+
+ flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ objective.context().ifPresent(context -> context.onSuccess(objective));
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ objective.context()
+ .ifPresent(context ->
+ context.onError(objective,
+ ObjectiveError.FLOWINSTALLATIONFAILED));
+ }
+ }));
+ }
+
+ @Override
+ public void next(NextObjective nextObjective) {
+
+ pendingNext.put(nextObjective.id(), nextObjective);
+ flowObjectiveStore.putNextGroup(nextObjective.id(),
+ new SingleGroup(
+ new DefaultGroupKey(
+ appKryo.serialize(nextObjective.id()))));
+ nextObjective.context().ifPresent(context -> context.onSuccess(nextObjective));
+ }
+
+ @Override
+ public List<String> getNextMappings(NextGroup nextGroup) {
+ // Default single table pipeline does not use nextObjectives or groups
+ return null;
+ }
+
+ private class SingleGroup implements NextGroup {
+
+ private final GroupKey key;
+
+ public SingleGroup(GroupKey key) {
+ this.key = key;
+ }
+
+ public GroupKey key() {
+ return key;
+ }
+
+ @Override
+ public byte[] data() {
+ return appKryo.serialize(key);
+ }
+ }
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManager.java
new file mode 100644
index 0000000..f501696
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManager.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchRequest;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleOperation;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED;
+
+/**
+ * Flow rule service implementation built on the virtual network service.
+ */
+public class VirtualNetworkFlowRuleManager
+ extends AbstractVirtualListenerManager<FlowRuleEvent, FlowRuleListener>
+ implements FlowRuleService {
+
+ private static final String VIRTUAL_FLOW_OP_TOPIC = "virtual-flow-ops-ids";
+ private static final String THREAD_GROUP_NAME = "onos/virtual-flowservice";
+ private static final String DEVICE_INSTALLER_PATTERN = "device-installer-%d";
+ private static final String OPERATION_PATTERN = "operations-%d";
+ public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final VirtualNetworkFlowRuleStore store;
+ private final DeviceService deviceService;
+
+ protected ExecutorService deviceInstallers =
+ Executors.newFixedThreadPool(32,
+ groupedThreads(THREAD_GROUP_NAME,
+ DEVICE_INSTALLER_PATTERN, log));
+ protected ExecutorService operationsService =
+ Executors.newFixedThreadPool(32,
+ groupedThreads(THREAD_GROUP_NAME,
+ OPERATION_PATTERN, log));
+ private IdGenerator idGenerator;
+
+ private final Map<Long, FlowOperationsProcessor> pendingFlowOperations = new ConcurrentHashMap<>();
+
+ private VirtualProviderRegistryService providerRegistryService = null;
+ private InternalFlowRuleProviderService innerProviderService = null;
+
+ private final FlowRuleStoreDelegate storeDelegate;
+
+ /**
+ * Creates a new VirtualNetworkFlowRuleService object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkFlowRuleManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId, FlowRuleEvent.class);
+
+ store = serviceDirectory.get(VirtualNetworkFlowRuleStore.class);
+
+ idGenerator = serviceDirectory.get(CoreService.class)
+ .getIdGenerator(VIRTUAL_FLOW_OP_TOPIC + networkId().toString());
+ providerRegistryService =
+ serviceDirectory.get(VirtualProviderRegistryService.class);
+ innerProviderService = new InternalFlowRuleProviderService();
+ providerRegistryService.registerProviderService(networkId(), innerProviderService);
+
+ this.deviceService = manager.get(networkId, DeviceService.class);
+ this.storeDelegate = new InternalStoreDelegate();
+ store.setDelegate(networkId, this.storeDelegate);
+ }
+
+ @Override
+ public int getFlowRuleCount() {
+ return store.getFlowRuleCount(networkId());
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ return store.getFlowEntries(networkId(), deviceId);
+ }
+
+ @Override
+ public void applyFlowRules(FlowRule... flowRules) {
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ for (FlowRule flowRule : flowRules) {
+ builder.add(flowRule);
+ }
+ apply(builder.build());
+ }
+
+ @Override
+ public void purgeFlowRules(DeviceId deviceId) {
+ store.purgeFlowRule(networkId(), deviceId);
+ }
+
+ @Override
+ public void removeFlowRules(FlowRule... flowRules) {
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ for (FlowRule flowRule : flowRules) {
+ builder.remove(flowRule);
+ }
+ apply(builder.build());
+ }
+
+ @Override
+ public void removeFlowRulesById(ApplicationId id) {
+ removeFlowRules(Iterables.toArray(getFlowRulesById(id), FlowRule.class));
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
+ DeviceService deviceService = manager.get(networkId(), DeviceService.class);
+
+ Set<FlowRule> flowEntries = Sets.newHashSet();
+ for (Device d : deviceService.getDevices()) {
+ for (FlowEntry flowEntry : store.getFlowEntries(networkId(), d.id())) {
+ if (flowEntry.appId() == id.id()) {
+ flowEntries.add(flowEntry);
+ }
+ }
+ }
+ return flowEntries;
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntriesById(ApplicationId id) {
+ DeviceService deviceService = manager.get(networkId(), DeviceService.class);
+
+ Set<FlowEntry> flowEntries = Sets.newHashSet();
+ for (Device d : deviceService.getDevices()) {
+ for (FlowEntry flowEntry : store.getFlowEntries(networkId(), d.id())) {
+ if (flowEntry.appId() == id.id()) {
+ flowEntries.add(flowEntry);
+ }
+ }
+ }
+ return flowEntries;
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) {
+ DeviceService deviceService = manager.get(networkId(), DeviceService.class);
+
+ Set<FlowRule> matches = Sets.newHashSet();
+ long toLookUp = ((long) appId.id() << 16) | groupId;
+ for (Device d : deviceService.getDevices()) {
+ for (FlowEntry flowEntry : store.getFlowEntries(networkId(), d.id())) {
+ if ((flowEntry.id().value() >>> 32) == toLookUp) {
+ matches.add(flowEntry);
+ }
+ }
+ }
+ return matches;
+ }
+
+ @Override
+ public void apply(FlowRuleOperations ops) {
+ operationsService.execute(new FlowOperationsProcessor(ops));
+ }
+
+ @Override
+ public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) {
+ return store.getTableStatistics(networkId(), deviceId);
+ }
+
+ private static FlowRuleBatchEntry.FlowRuleOperation mapOperationType(FlowRuleOperation.Type input) {
+ switch (input) {
+ case ADD:
+ return FlowRuleBatchEntry.FlowRuleOperation.ADD;
+ case MODIFY:
+ return FlowRuleBatchEntry.FlowRuleOperation.MODIFY;
+ case REMOVE:
+ return FlowRuleBatchEntry.FlowRuleOperation.REMOVE;
+ default:
+ throw new UnsupportedOperationException("Unknown flow rule type " + input);
+ }
+ }
+
+ private class FlowOperationsProcessor implements Runnable {
+ // Immutable
+ private final FlowRuleOperations fops;
+
+ // Mutable
+ private final List<Set<FlowRuleOperation>> stages;
+ private final Set<DeviceId> pendingDevices = new HashSet<>();
+ private boolean hasFailed = false;
+
+ FlowOperationsProcessor(FlowRuleOperations ops) {
+ this.stages = Lists.newArrayList(ops.stages());
+ this.fops = ops;
+ }
+
+ @Override
+ public synchronized void run() {
+ if (!stages.isEmpty()) {
+ process(stages.remove(0));
+ } else if (!hasFailed) {
+ fops.callback().onSuccess(fops);
+ }
+ }
+
+ private void process(Set<FlowRuleOperation> ops) {
+ Multimap<DeviceId, FlowRuleBatchEntry> perDeviceBatches = ArrayListMultimap.create();
+
+ for (FlowRuleOperation op : ops) {
+ perDeviceBatches.put(op.rule().deviceId(),
+ new FlowRuleBatchEntry(mapOperationType(op.type()), op.rule()));
+ }
+ pendingDevices.addAll(perDeviceBatches.keySet());
+
+ for (DeviceId deviceId : perDeviceBatches.keySet()) {
+ long id = idGenerator.getNewId();
+ final FlowRuleBatchOperation b = new FlowRuleBatchOperation(perDeviceBatches.get(deviceId),
+ deviceId, id);
+ pendingFlowOperations.put(id, this);
+ deviceInstallers.execute(() -> store.storeBatch(networkId(), b));
+ }
+ }
+
+ synchronized void satisfy(DeviceId devId) {
+ pendingDevices.remove(devId);
+ if (pendingDevices.isEmpty()) {
+ operationsService.execute(this);
+ }
+ }
+
+ synchronized void fail(DeviceId devId, Set<? extends FlowRule> failures) {
+ hasFailed = true;
+ pendingDevices.remove(devId);
+ if (pendingDevices.isEmpty()) {
+ operationsService.execute(this);
+ }
+
+ FlowRuleOperations.Builder failedOpsBuilder = FlowRuleOperations.builder();
+ failures.forEach(failedOpsBuilder::add);
+
+ fops.callback().onError(failedOpsBuilder.build());
+ }
+ }
+
+ private final class InternalFlowRuleProviderService
+ extends AbstractVirtualProviderService<VirtualFlowRuleProvider>
+ implements VirtualFlowRuleProviderService {
+
+ final Map<FlowEntry, Long> firstSeen = Maps.newConcurrentMap();
+ final Map<FlowEntry, Long> lastSeen = Maps.newConcurrentMap();
+
+ private InternalFlowRuleProviderService() {
+ //TODO: find a proper virtual provider.
+ Set<ProviderId> providerIds =
+ providerRegistryService.getProvidersByService(this);
+ ProviderId providerId = providerIds.stream().findFirst().get();
+ VirtualFlowRuleProvider provider = (VirtualFlowRuleProvider)
+ providerRegistryService.getProvider(providerId);
+ setProvider(provider);
+ }
+
+ @Override
+ public void flowRemoved(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
+ checkValidity();
+
+ lastSeen.remove(flowEntry);
+ firstSeen.remove(flowEntry);
+ FlowEntry stored = store.getFlowEntry(networkId(), flowEntry);
+ if (stored == null) {
+ log.debug("Rule already evicted from store: {}", flowEntry);
+ return;
+ }
+ if (flowEntry.reason() == FlowEntry.FlowRemoveReason.HARD_TIMEOUT) {
+ ((DefaultFlowEntry) stored).setState(FlowEntry.FlowEntryState.REMOVED);
+ }
+
+ //FIXME: obtains provider from devices providerId()
+ FlowRuleEvent event = null;
+ switch (stored.state()) {
+ case ADDED:
+ case PENDING_ADD:
+ provider().applyFlowRule(networkId(), stored);
+ break;
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(networkId(), stored);
+ break;
+ default:
+ break;
+
+ }
+ if (event != null) {
+ log.debug("Flow {} removed", flowEntry);
+ post(event);
+ }
+ }
+
+ private void flowMissing(FlowEntry flowRule) {
+ checkNotNull(flowRule, FLOW_RULE_NULL);
+ checkValidity();
+
+ FlowRuleEvent event = null;
+ switch (flowRule.state()) {
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(networkId(), flowRule);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ event = store.pendingFlowRule(networkId(), flowRule);
+
+ try {
+ provider().applyFlowRule(networkId(), flowRule);
+ } catch (UnsupportedOperationException e) {
+ log.warn(e.getMessage());
+ if (flowRule instanceof DefaultFlowEntry) {
+ //FIXME modification of "stored" flow entry outside of store
+ ((DefaultFlowEntry) flowRule).setState(FlowEntry.FlowEntryState.FAILED);
+ }
+ }
+ break;
+ default:
+ log.debug("Flow {} has not been installed.", flowRule);
+ }
+
+ if (event != null) {
+ log.debug("Flow {} removed", flowRule);
+ post(event);
+ }
+ }
+
+ private void extraneousFlow(FlowRule flowRule) {
+ checkNotNull(flowRule, FLOW_RULE_NULL);
+ checkValidity();
+
+ provider().removeFlowRule(networkId(), flowRule);
+ log.debug("Flow {} is on switch but not in store.", flowRule);
+ }
+
+ private void flowAdded(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
+
+ if (checkRuleLiveness(flowEntry, store.getFlowEntry(networkId(), flowEntry))) {
+ FlowRuleEvent event = store.addOrUpdateFlowRule(networkId(), flowEntry);
+ if (event == null) {
+ log.debug("No flow store event generated.");
+ } else {
+ log.trace("Flow {} {}", flowEntry, event.type());
+ post(event);
+ }
+ } else {
+ log.debug("Removing flow rules....");
+ removeFlowRules(flowEntry);
+ }
+ }
+
+ private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
+ if (storedRule == null) {
+ return false;
+ }
+ if (storedRule.isPermanent()) {
+ return true;
+ }
+
+ final long timeout = storedRule.timeout() * 1000L;
+ final long currentTime = System.currentTimeMillis();
+
+ // Checking flow with hardTimeout
+ if (storedRule.hardTimeout() != 0) {
+ if (!firstSeen.containsKey(storedRule)) {
+ // First time rule adding
+ firstSeen.put(storedRule, currentTime);
+ } else {
+ Long first = firstSeen.get(storedRule);
+ final long hardTimeout = storedRule.hardTimeout() * 1000L;
+ if ((currentTime - first) > hardTimeout) {
+ return false;
+ }
+ }
+ }
+
+ if (storedRule.packets() != swRule.packets()) {
+ lastSeen.put(storedRule, currentTime);
+ return true;
+ }
+ if (!lastSeen.containsKey(storedRule)) {
+ // checking for the first time
+ lastSeen.put(storedRule, storedRule.lastSeen());
+ // Use following if lastSeen attr. was removed.
+ //lastSeen.put(storedRule, currentTime);
+ }
+ Long last = lastSeen.get(storedRule);
+
+ // concurrently removed? let the liveness check fail
+ return last != null && (currentTime - last) <= timeout;
+ }
+
+ @Override
+ public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+ pushFlowMetricsInternal(deviceId, flowEntries, true);
+ }
+
+ @Override
+ public void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+ pushFlowMetricsInternal(deviceId, flowEntries, false);
+ }
+
+ private void pushFlowMetricsInternal(DeviceId deviceId, Iterable<FlowEntry> flowEntries,
+ boolean useMissingFlow) {
+ Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap();
+ store.getFlowEntries(networkId(), deviceId).forEach(f -> storedRules.put(f, f));
+
+ for (FlowEntry rule : flowEntries) {
+ try {
+ FlowEntry storedRule = storedRules.remove(rule);
+ if (storedRule != null) {
+ if (storedRule.id().equals(rule.id())) {
+ // we both have the rule, let's update some info then.
+ flowAdded(rule);
+ } else {
+ // the two rules are not an exact match - remove the
+ // switch's rule and install our rule
+ extraneousFlow(rule);
+ flowMissing(storedRule);
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Can't process added or extra rule {}", e.getMessage());
+ }
+ }
+
+ // DO NOT reinstall
+ if (useMissingFlow) {
+ for (FlowEntry rule : storedRules.keySet()) {
+ try {
+ // there are rules in the store that aren't on the switch
+ log.debug("Adding rule in store, but not on switch {}", rule);
+ flowMissing(rule);
+ } catch (Exception e) {
+ log.debug("Can't add missing flow rule:", e);
+ }
+ }
+ }
+ }
+
+ public void batchOperationCompleted(long batchId, CompletedBatchOperation operation) {
+ store.batchOperationComplete(networkId(), FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(batchId, Collections.emptySet()),
+ operation
+ ));
+ }
+
+ @Override
+ public void pushTableStatistics(DeviceId deviceId,
+ List<TableStatisticsEntry> tableStats) {
+ store.updateTableStatistics(networkId(), deviceId, tableStats);
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements FlowRuleStoreDelegate {
+
+ // TODO: Right now we only dispatch events at individual flowEntry level.
+ // It may be more efficient for also dispatch events as a batch.
+ @Override
+ public void notify(FlowRuleBatchEvent event) {
+ final FlowRuleBatchRequest request = event.subject();
+ switch (event.type()) {
+ case BATCH_OPERATION_REQUESTED:
+ // Request has been forwarded to MASTER Node, and was
+ request.ops().forEach(
+ op -> {
+ switch (op.operator()) {
+ case ADD:
+ post(new FlowRuleEvent(RULE_ADD_REQUESTED, op.target()));
+ break;
+ case REMOVE:
+ post(new FlowRuleEvent(RULE_REMOVE_REQUESTED, op.target()));
+ break;
+ case MODIFY:
+ //TODO: do something here when the time comes.
+ break;
+ default:
+ log.warn("Unknown flow operation operator: {}", op.operator());
+ }
+ }
+ );
+
+ DeviceId deviceId = event.deviceId();
+ FlowRuleBatchOperation batchOperation = request.asBatchOperation(deviceId);
+
+ VirtualFlowRuleProvider provider = innerProviderService.provider();
+ if (provider != null) {
+ provider.executeBatch(networkId, batchOperation);
+ }
+
+ break;
+
+ case BATCH_OPERATION_COMPLETED:
+ FlowOperationsProcessor fops = pendingFlowOperations.remove(
+ event.subject().batchId());
+ if (fops == null) {
+ return;
+ }
+
+ if (event.result().isSuccess()) {
+ fops.satisfy(event.deviceId());
+ } else {
+ fops.fail(event.deviceId(), event.result().failedItems());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManager.java
new file mode 100644
index 0000000..5fed4a8
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManager.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualGroupProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualGroupProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Group service implementation built on the virtual network service.
+ */
+public class VirtualNetworkGroupManager
+ extends AbstractVirtualListenerManager<GroupEvent, GroupListener>
+ implements GroupService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final VirtualNetworkGroupStore store;
+
+ private VirtualProviderRegistryService providerRegistryService = null;
+ private VirtualGroupProviderService innerProviderService;
+ private InternalStoreDelegate storeDelegate;
+ private DeviceService deviceService;
+
+ //TODO: make this configurable
+ private boolean purgeOnDisconnection = false;
+
+ public VirtualNetworkGroupManager(VirtualNetworkService manager, NetworkId networkId) {
+ super(manager, networkId, GroupEvent.class);
+
+ store = serviceDirectory.get(VirtualNetworkGroupStore.class);
+ deviceService = manager.get(networkId, DeviceService.class);
+
+ providerRegistryService =
+ serviceDirectory.get(VirtualProviderRegistryService.class);
+ innerProviderService = new InternalGroupProviderService();
+ providerRegistryService.registerProviderService(networkId(), innerProviderService);
+
+ this.storeDelegate = new InternalStoreDelegate();
+ store.setDelegate(networkId, this.storeDelegate);
+
+ log.info("Started");
+ }
+
+ @Override
+ public void addGroup(GroupDescription groupDesc) {
+ store.storeGroupDescription(networkId(), groupDesc);
+ }
+
+ @Override
+ public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
+ return store.getGroup(networkId(), deviceId, appCookie);
+ }
+
+ @Override
+ public void addBucketsToGroup(DeviceId deviceId, GroupKey oldCookie, GroupBuckets buckets,
+ GroupKey newCookie, ApplicationId appId) {
+ store.updateGroupDescription(networkId(),
+ deviceId,
+ oldCookie,
+ VirtualNetworkGroupStore.UpdateType.ADD,
+ buckets,
+ newCookie);
+ }
+
+ @Override
+ public void removeBucketsFromGroup(DeviceId deviceId, GroupKey oldCookie,
+ GroupBuckets buckets, GroupKey newCookie,
+ ApplicationId appId) {
+ store.updateGroupDescription(networkId(),
+ deviceId,
+ oldCookie,
+ VirtualNetworkGroupStore.UpdateType.REMOVE,
+ buckets,
+ newCookie);
+
+ }
+
+ @Override
+ public void setBucketsForGroup(DeviceId deviceId,
+ GroupKey oldCookie,
+ GroupBuckets buckets,
+ GroupKey newCookie,
+ ApplicationId appId) {
+ store.updateGroupDescription(networkId(),
+ deviceId,
+ oldCookie,
+ VirtualNetworkGroupStore.UpdateType.SET,
+ buckets,
+ newCookie);
+ }
+
+ @Override
+ public void purgeGroupEntries(DeviceId deviceId) {
+ store.purgeGroupEntry(networkId(), deviceId);
+ }
+
+ @Override
+ public void purgeGroupEntries() {
+ store.purgeGroupEntries(networkId());
+ }
+
+ @Override
+ public void removeGroup(DeviceId deviceId, GroupKey appCookie, ApplicationId appId) {
+ store.deleteGroupDescription(networkId(), deviceId, appCookie);
+ }
+
+ @Override
+ public Iterable<Group> getGroups(DeviceId deviceId, ApplicationId appId) {
+ return store.getGroups(networkId(), deviceId);
+ }
+
+ @Override
+ public Iterable<Group> getGroups(DeviceId deviceId) {
+ return store.getGroups(networkId(), deviceId);
+ }
+
+ private class InternalGroupProviderService
+ extends AbstractVirtualProviderService<VirtualGroupProvider>
+ implements VirtualGroupProviderService {
+
+ protected InternalGroupProviderService() {
+ Set<ProviderId> providerIds =
+ providerRegistryService.getProvidersByService(this);
+ ProviderId providerId = providerIds.stream().findFirst().get();
+ VirtualGroupProvider provider = (VirtualGroupProvider)
+ providerRegistryService.getProvider(providerId);
+ setProvider(provider);
+ }
+
+ @Override
+ public void groupOperationFailed(DeviceId deviceId,
+ GroupOperation operation) {
+ store.groupOperationFailed(networkId(), deviceId, operation);
+ }
+
+ @Override
+ public void pushGroupMetrics(DeviceId deviceId, Collection<Group> groupEntries) {
+ log.trace("Received group metrics from device {}", deviceId);
+ checkValidity();
+ store.pushGroupMetrics(networkId(), deviceId, groupEntries);
+ }
+
+ @Override
+ public void notifyOfFailovers(Collection<Group> failoverGroups) {
+ store.notifyOfFailovers(networkId(), failoverGroups);
+ }
+ }
+
+ private class InternalStoreDelegate implements GroupStoreDelegate {
+ @Override
+ public void notify(GroupEvent event) {
+ final Group group = event.subject();
+ VirtualGroupProvider groupProvider = innerProviderService.provider();
+ GroupOperations groupOps = null;
+ switch (event.type()) {
+ case GROUP_ADD_REQUESTED:
+ log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupAddOp = GroupOperation.
+ createAddGroupOperation(group.id(),
+ group.type(),
+ group.buckets());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupAddOp));
+ groupProvider.performGroupOperation(networkId(), group.deviceId(),
+ groupOps);
+ break;
+
+ case GROUP_UPDATE_REQUESTED:
+ log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupModifyOp = GroupOperation.
+ createModifyGroupOperation(group.id(),
+ group.type(),
+ group.buckets());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupModifyOp));
+ groupProvider.performGroupOperation(networkId(), group.deviceId(),
+ groupOps);
+ break;
+
+ case GROUP_REMOVE_REQUESTED:
+ log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupDeleteOp = GroupOperation.
+ createDeleteGroupOperation(group.id(),
+ group.type());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupDeleteOp));
+ groupProvider.performGroupOperation(networkId(), group.deviceId(),
+ groupOps);
+ break;
+
+ case GROUP_ADDED:
+ case GROUP_UPDATED:
+ case GROUP_REMOVED:
+ case GROUP_ADD_FAILED:
+ case GROUP_UPDATE_FAILED:
+ case GROUP_REMOVE_FAILED:
+ case GROUP_BUCKET_FAILOVER:
+ post(event);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_REMOVED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ DeviceId deviceId = event.subject().id();
+ if (!deviceService.isAvailable(deviceId)) {
+ log.debug("Device {} became un available; clearing initial audit status",
+ event.type(), event.subject().id());
+ store.deviceInitialAuditCompleted(networkId(), event.subject().id(), false);
+
+ if (purgeOnDisconnection) {
+ store.purgeGroupEntry(networkId(), deviceId);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManager.java
new file mode 100644
index 0000000..5d14d4a
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManager.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Host service implementation built on the virtual network service.
+ */
+public class VirtualNetworkHostManager
+ extends AbstractVirtualListenerManager<HostEvent, HostListener>
+ implements HostService {
+
+ private static final String HOST_NULL = "Host ID cannot be null";
+
+ /**
+ * Creates a new virtual network host service object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkHostManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId, HostEvent.class);
+ }
+
+
+ @Override
+ public int getHostCount() {
+ return manager.getVirtualHosts(this.networkId()).size();
+ }
+
+ @Override
+ public Iterable<Host> getHosts() {
+ return getHostsColl();
+ }
+
+ @Override
+ public Host getHost(HostId hostId) {
+ checkNotNull(hostId, HOST_NULL);
+ Optional<VirtualHost> foundHost =
+ manager.getVirtualHosts(this.networkId())
+ .stream()
+ .filter(host -> hostId.equals(host.id()))
+ .findFirst();
+ if (foundHost.isPresent()) {
+ return foundHost.get();
+ }
+ return null;
+ }
+
+ /**
+ * Gets a collection of virtual hosts.
+ *
+ * @return collection of virtual hosts.
+ */
+ private Collection<Host> getHostsColl() {
+ return manager.getVirtualHosts(this.networkId())
+ .stream().collect(Collectors.toSet());
+ }
+
+ /**
+ * Filters specified collection.
+ *
+ * @param collection collection of hosts to filter
+ * @param predicate condition to filter on
+ * @return collection of virtual hosts that satisfy the filter condition
+ */
+ private Set<Host> filter(Collection<Host> collection, Predicate<Host> predicate) {
+ return collection.stream().filter(predicate).collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Host> getHostsByVlan(VlanId vlanId) {
+ checkNotNull(vlanId, "VLAN identifier cannot be null");
+ return filter(getHostsColl(), host -> Objects.equals(host.vlan(), vlanId));
+ }
+
+ @Override
+ public Set<Host> getHostsByMac(MacAddress mac) {
+ checkNotNull(mac, "MAC address cannot be null");
+ return filter(getHostsColl(), host -> Objects.equals(host.mac(), mac));
+ }
+
+ @Override
+ public Set<Host> getHostsByIp(IpAddress ip) {
+ checkNotNull(ip, "IP address cannot be null");
+ return filter(getHostsColl(), host -> host.ipAddresses().contains(ip));
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, "Connect point cannot be null");
+ return filter(getHostsColl(), host -> host.location().equals(connectPoint));
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(DeviceId deviceId) {
+ checkNotNull(deviceId, "Device identifier cannot be null");
+ return filter(getHostsColl(), host -> host.location().deviceId().equals(deviceId));
+ }
+
+ @Override
+ public void startMonitoringIp(IpAddress ip) {
+ //TODO check what needs to be done here
+ }
+
+ @Override
+ public void stopMonitoringIp(IpAddress ip) {
+ //TODO check what needs to be done here
+ }
+
+ @Override
+ public void requestMac(IpAddress ip) {
+ //TODO check what needs to be done here
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
new file mode 100644
index 0000000..f4c1137
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onlab.util.Tools;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.VnetService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualFinalIntentProcessPhase;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallCoordinator;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentAccumulator;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentCompilerRegistry;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallerRegistry;
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentSkipped;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentBatchDelegate;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.resource.ResourceConsumer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.*;
+import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
+import static org.onlab.util.BoundedThreadPool.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.newInitialPhase;
+import static org.onosproject.net.intent.IntentState.FAILED;
+
+/**
+ * Intent service implementation built on the virtual network service.
+ */
+public class VirtualNetworkIntentManager
+ extends AbstractVirtualListenerManager<IntentEvent, IntentListener>
+ implements IntentService, VnetService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int DEFAULT_NUM_THREADS = 12;
+ private int numThreads = DEFAULT_NUM_THREADS;
+
+ private static final String NETWORK_ID_NULL = "Network ID cannot be null";
+ private static final String DEVICE_NULL = "Device cannot be null";
+ private static final String INTENT_NULL = "Intent cannot be null";
+ private static final String KEY_NULL = "Key cannot be null";
+ private static final String APP_ID_NULL = "Intent app identifier cannot be null";
+ private static final String INTENT_KEY_NULL = "Intent key cannot be null";
+ private static final String CP_NULL = "Connect Point cannot be null";
+
+ //FIXME: Tracker service for vnet.
+
+ //ONOS core services
+ protected VirtualNetworkStore virtualNetworkStore;
+ protected VirtualNetworkIntentStore intentStore;
+
+ //Virtual network services
+ protected GroupService groupService;
+
+ private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
+ private final InternalIntentProcessor processor = new InternalIntentProcessor();
+ private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+ private final VirtualIntentCompilerRegistry compilerRegistry =
+ VirtualIntentCompilerRegistry.getInstance();
+ private final VirtualIntentInstallerRegistry installerRegistry =
+ VirtualIntentInstallerRegistry.getInstance();
+ private final VirtualIntentAccumulator accumulator =
+ new VirtualIntentAccumulator(batchDelegate);
+
+ private VirtualIntentInstallCoordinator installCoordinator;
+ private ExecutorService batchExecutor;
+ private ExecutorService workerExecutor;
+
+ /**
+ * Creates a new VirtualNetworkIntentService object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkIntentManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+
+ super(virtualNetworkManager, networkId, IntentEvent.class);
+
+ this.virtualNetworkStore = serviceDirectory.get(VirtualNetworkStore.class);
+ this.intentStore = serviceDirectory.get(VirtualNetworkIntentStore.class);
+
+ this.groupService = manager.get(networkId, GroupService.class);
+
+ intentStore.setDelegate(networkId, delegate);
+ batchExecutor = newSingleThreadExecutor(groupedThreads("onos/intent", "batch", log));
+ workerExecutor = newFixedThreadPool(numThreads, groupedThreads("onos/intent", "worker-%d", log));
+
+ installCoordinator = new VirtualIntentInstallCoordinator(networkId, installerRegistry, intentStore);
+ log.info("Started");
+
+ }
+
+ @Override
+ public void submit(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ checkState(intent instanceof VirtualNetworkIntent, "Only VirtualNetworkIntent is supported.");
+ checkArgument(validateIntent((VirtualNetworkIntent) intent), "Invalid Intent");
+
+ IntentData data = IntentData.submit(intent);
+ intentStore.addPending(networkId, data);
+ }
+
+ /**
+ * Returns true if the virtual network intent is valid.
+ *
+ * @param intent virtual network intent
+ * @return true if intent is valid
+ */
+ private boolean validateIntent(VirtualNetworkIntent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ checkNotNull(intent.networkId(), NETWORK_ID_NULL);
+ checkNotNull(intent.appId(), APP_ID_NULL);
+ checkNotNull(intent.key(), INTENT_KEY_NULL);
+ ConnectPoint ingressPoint = intent.ingressPoint();
+ ConnectPoint egressPoint = intent.egressPoint();
+
+ return (validateConnectPoint(ingressPoint) && validateConnectPoint(egressPoint));
+ }
+
+ /**
+ * Returns true if the connect point is valid.
+ *
+ * @param connectPoint connect point
+ * @return true if connect point is valid
+ */
+ private boolean validateConnectPoint(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CP_NULL);
+ Port port = getPort(connectPoint.deviceId(), connectPoint.port());
+ return port != null;
+ }
+
+ /**
+ * Returns the virtual port for the given device identifier and port number.
+ *
+ * @param deviceId virtual device identifier
+ * @param portNumber virtual port number
+ * @return virtual port
+ */
+ private Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ checkNotNull(deviceId, DEVICE_NULL);
+
+ Optional<VirtualPort> foundPort = manager.getVirtualPorts(this.networkId(), deviceId)
+ .stream()
+ .filter(port -> port.number().equals(portNumber))
+ .findFirst();
+ if (foundPort.isPresent()) {
+ return foundPort.get();
+ }
+ return null;
+ }
+
+ @Override
+ public void withdraw(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ IntentData data = IntentData.withdraw(intent);
+ intentStore.addPending(networkId, data);
+ }
+
+ @Override
+ public void purge(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+
+ IntentData data = IntentData.purge(intent);
+ intentStore.addPending(networkId, data);
+
+ // remove associated group if there is one
+ // FIXME: Remove P2P intent for vnets
+ }
+
+ @Override
+ public Intent getIntent(Key key) {
+ checkNotNull(key, KEY_NULL);
+ return intentStore.getIntent(networkId, key);
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ return intentStore.getIntents(networkId);
+ }
+
+ @Override
+ public void addPending(IntentData intentData) {
+ checkNotNull(intentData, INTENT_NULL);
+ //TODO we might consider further checking / assertions
+ intentStore.addPending(networkId, intentData);
+ }
+
+ @Override
+ public Iterable<IntentData> getIntentData() {
+ return intentStore.getIntentData(networkId, false, 0);
+ }
+
+ @Override
+ public long getIntentCount() {
+ return intentStore.getIntentCount(networkId);
+ }
+
+ @Override
+ public IntentState getIntentState(Key intentKey) {
+ checkNotNull(intentKey, KEY_NULL);
+ return intentStore.getIntentState(networkId, intentKey);
+ }
+
+ @Override
+ public List<Intent> getInstallableIntents(Key intentKey) {
+ return intentStore.getInstallableIntents(networkId, intentKey);
+ }
+
+ @Override
+ public boolean isLocal(Key intentKey) {
+ return intentStore.isMaster(networkId, intentKey);
+ }
+
+ @Override
+ public Iterable<Intent> getPending() {
+ return intentStore.getPending(networkId);
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements IntentStoreDelegate {
+ @Override
+ public void notify(IntentEvent event) {
+ post(event);
+ switch (event.type()) {
+ case WITHDRAWN:
+ //FIXME: release resources
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void process(IntentData data) {
+ accumulator.add(data);
+ }
+
+ @Override
+ public void onUpdate(IntentData intentData) {
+ //FIXME: track intent
+ }
+
+ private void releaseResources(Intent intent) {
+ // If a resource group is set on the intent, the resource consumer is
+ // set equal to it. Otherwise it's set to the intent key
+ ResourceConsumer resourceConsumer =
+ intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
+
+ // By default the resource doesn't get released
+ boolean removeResource = false;
+
+ if (intent.resourceGroup() == null) {
+ // If the intent doesn't have a resource group, it means the
+ // resource was registered using the intent key, so it can be
+ // released
+ removeResource = true;
+ } else {
+ // When a resource group is set, we make sure there are no other
+ // intents using the same resource group, before deleting the
+ // related resources.
+ Long remainingIntents =
+ Tools.stream(intentStore.getIntents(networkId))
+ .filter(i -> {
+ return i.resourceGroup() != null
+ && i.resourceGroup().equals(intent.resourceGroup());
+ })
+ .count();
+ if (remainingIntents == 0) {
+ removeResource = true;
+ }
+ }
+
+ if (removeResource) {
+ // Release resources allocated to withdrawn intent
+ // FIXME: confirm resources are released
+ }
+ }
+ }
+
+ private class InternalBatchDelegate implements IntentBatchDelegate {
+ @Override
+ public void execute(Collection<IntentData> operations) {
+ log.debug("Execute {} operation(s).", operations.size());
+ log.trace("Execute operations: {}", operations);
+
+ // batchExecutor is single-threaded, so only one batch is in flight at a time
+ CompletableFuture.runAsync(() -> {
+ // process intent until the phase reaches one of the final phases
+ List<CompletableFuture<IntentData>> futures = operations.stream()
+ .map(data -> {
+ log.debug("Start processing of {} {}@{}", data.request(), data.key(), data.version());
+ return data;
+ })
+ .map(x -> CompletableFuture.completedFuture(x)
+ .thenApply(VirtualNetworkIntentManager.this::createInitialPhase)
+ .thenApplyAsync(VirtualIntentProcessPhase::process, workerExecutor)
+ .thenApply(VirtualFinalIntentProcessPhase::data)
+ .exceptionally(e -> {
+ // When the future fails, we update the Intent to simulate the failure of
+ // the installation/withdrawal phase and we save in the current map. In
+ // the next round the CleanUp Thread will pick this Intent again.
+ log.warn("Future failed", e);
+ log.warn("Intent {} - state {} - request {}",
+ x.key(), x.state(), x.request());
+ switch (x.state()) {
+ case INSTALL_REQ:
+ case INSTALLING:
+ case WITHDRAW_REQ:
+ case WITHDRAWING:
+ // TODO should we swtich based on current
+ IntentData current = intentStore.getIntentData(networkId, x.key());
+ return IntentData.nextState(current, FAILED);
+ default:
+ return null;
+ }
+ }))
+ .collect(Collectors.toList());
+
+ // write multiple data to store in order
+ intentStore.batchWrite(networkId, Tools.allOf(futures).join().stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ }, batchExecutor).exceptionally(e -> {
+ log.error("Error submitting batches:", e);
+ // FIXME incomplete Intents should be cleaned up
+ // (transition to FAILED, etc.)
+
+ // the batch has failed
+ // TODO: maybe we should do more?
+ log.error("Walk the plank, matey...");
+ return null;
+ }).thenRun(accumulator::ready);
+
+ }
+ }
+
+ private VirtualIntentProcessPhase createInitialPhase(IntentData data) {
+ IntentData pending = intentStore.getPendingData(networkId, data.key());
+ if (pending == null || pending.version().isNewerThan(data.version())) {
+ /*
+ If the pending map is null, then this intent was compiled by a
+ previous batch iteration, so we can skip it.
+ If the pending map has a newer request, it will get compiled as
+ part of the next batch, so we can skip it.
+ */
+ return VirtualIntentSkipped.getPhase();
+ }
+ IntentData current = intentStore.getIntentData(networkId, data.key());
+ return newInitialPhase(networkId, processor, data, current);
+ }
+
+ private class InternalIntentProcessor implements VirtualIntentProcessor {
+ @Override
+ public List<Intent> compile(NetworkId networkId,
+ Intent intent,
+ List<Intent> previousInstallables) {
+ return compilerRegistry.compile(networkId, intent, previousInstallables);
+ }
+
+ @Override
+ public void apply(NetworkId networkId,
+ Optional<IntentData> toUninstall,
+ Optional<IntentData> toInstall) {
+
+ installCoordinator.installIntents(toUninstall, toInstall);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManager.java
new file mode 100644
index 0000000..1a1c2bf
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkService;
+
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Link service implementation built on the virtual network service.
+ */
+public class VirtualNetworkLinkManager
+ extends AbstractVirtualListenerManager<LinkEvent, LinkListener>
+ implements LinkService {
+
+ private static final String DEVICE_NULL = "Device cannot be null";
+ private static final String CONNECT_POINT_NULL = "Connect point cannot be null";
+
+ /**
+ * Creates a new VirtualNetworkLinkService object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual networkIdentifier
+ */
+ public VirtualNetworkLinkManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId, LinkEvent.class);
+ }
+
+ @Override
+ public int getLinkCount() {
+ return manager.getVirtualLinks(this.networkId()).size();
+ }
+
+ @Override
+ public Iterable<Link> getLinks() {
+ return manager.getVirtualLinks(this.networkId())
+ .stream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Iterable<Link> getActiveLinks() {
+
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (link.state().equals(Link.State.ACTIVE)))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getDeviceLinks(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (deviceId.equals(link.src().elementId()) ||
+ deviceId.equals(link.dst().elementId())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (deviceId.equals(link.dst().elementId())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (deviceId.equals(link.src().elementId())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getLinks(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (connectPoint.equals(link.src()) ||
+ connectPoint.equals(link.dst())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (connectPoint.equals(link.dst())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<Link> getIngressLinks(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (connectPoint.equals(link.src())))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Link getLink(ConnectPoint src, ConnectPoint dst) {
+ checkNotNull(src, CONNECT_POINT_NULL);
+ checkNotNull(dst, CONNECT_POINT_NULL);
+ Optional<VirtualLink> foundLink = manager.getVirtualLinks(this.networkId())
+ .stream()
+ .filter(link -> (src.equals(link.src()) &&
+ dst.equals(link.dst())))
+ .findFirst();
+
+ if (foundLink.isPresent()) {
+ return foundLink.get();
+ }
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
new file mode 100644
index 0000000..3b4ed8c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.VnetService;
+import org.onosproject.incubator.net.virtual.event.VirtualEvent;
+import org.onosproject.incubator.net.virtual.event.VirtualListenerRegistryManager;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.TopologyService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of the virtual network service.
+ */
+@Component(service = {
+ VirtualNetworkService.class,
+ VirtualNetworkAdminService.class,
+ VirtualNetworkService.class,
+ VirtualNetworkProviderRegistry.class
+ })
+public class VirtualNetworkManager
+ extends AbstractListenerProviderRegistry<VirtualNetworkEvent,
+ VirtualNetworkListener, VirtualNetworkProvider, VirtualNetworkProviderService>
+ implements VirtualNetworkService, VirtualNetworkAdminService, VirtualNetworkProviderRegistry {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String TENANT_NULL = "Tenant ID cannot be null";
+ private static final String NETWORK_NULL = "Network ID cannot be null";
+ private static final String DEVICE_NULL = "Device ID cannot be null";
+ private static final String LINK_POINT_NULL = "Link end-point cannot be null";
+
+ private static final String VIRTUAL_NETWORK_APP_ID_STRING =
+ "org.onosproject.virtual-network";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualNetworkStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ private VirtualNetworkStoreDelegate delegate = this::post;
+
+ private ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+ private ApplicationId appId;
+
+ // TODO: figure out how to coordinate "implementation" of a virtual network in a cluster
+
+ /**
+ * Only used for Junit test methods outside of this package.
+ *
+ * @param store virtual network store
+ */
+ public void setStore(VirtualNetworkStore store) {
+ this.store = store;
+ }
+
+ @Activate
+ public void activate() {
+ eventDispatcher.addSink(VirtualNetworkEvent.class, listenerRegistry);
+ eventDispatcher.addSink(VirtualEvent.class,
+ VirtualListenerRegistryManager.getInstance());
+ store.setDelegate(delegate);
+ appId = coreService.registerApplication(VIRTUAL_NETWORK_APP_ID_STRING);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(VirtualNetworkEvent.class);
+ eventDispatcher.removeSink(VirtualEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void registerTenantId(TenantId tenantId) {
+ checkNotNull(tenantId, TENANT_NULL);
+ store.addTenantId(tenantId);
+ }
+
+ @Override
+ public void unregisterTenantId(TenantId tenantId) {
+ checkNotNull(tenantId, TENANT_NULL);
+ store.removeTenantId(tenantId);
+ }
+
+ @Override
+ public Set<TenantId> getTenantIds() {
+ return store.getTenantIds();
+ }
+
+ @Override
+ public VirtualNetwork createVirtualNetwork(TenantId tenantId) {
+ checkNotNull(tenantId, TENANT_NULL);
+ return store.addNetwork(tenantId);
+ }
+
+ @Override
+ public void removeVirtualNetwork(NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ store.removeNetwork(networkId);
+ }
+
+ @Override
+ public VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ return store.addDevice(networkId, deviceId);
+ }
+
+ @Override
+ public void removeVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ store.removeDevice(networkId, deviceId);
+ }
+
+ @Override
+ public VirtualHost createVirtualHost(NetworkId networkId, HostId hostId,
+ MacAddress mac, VlanId vlan,
+ HostLocation location, Set<IpAddress> ips) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(hostId, DEVICE_NULL);
+ return store.addHost(networkId, hostId, mac, vlan, location, ips);
+ }
+
+ @Override
+ public void removeVirtualHost(NetworkId networkId, HostId hostId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(hostId, DEVICE_NULL);
+ store.removeHost(networkId, hostId);
+ }
+
+ @Override
+ public VirtualLink createVirtualLink(NetworkId networkId,
+ ConnectPoint src, ConnectPoint dst) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(src, LINK_POINT_NULL);
+ checkNotNull(dst, LINK_POINT_NULL);
+ ConnectPoint physicalSrc = mapVirtualToPhysicalPort(networkId, src);
+ checkNotNull(physicalSrc, LINK_POINT_NULL);
+ ConnectPoint physicalDst = mapVirtualToPhysicalPort(networkId, dst);
+ checkNotNull(physicalDst, LINK_POINT_NULL);
+
+ VirtualNetworkProvider provider = getProvider(DefaultVirtualLink.PID);
+ Link.State state = Link.State.INACTIVE;
+ if (provider != null) {
+ boolean traversable = provider.isTraversable(physicalSrc, physicalDst);
+ state = traversable ? Link.State.ACTIVE : Link.State.INACTIVE;
+ }
+ return store.addLink(networkId, src, dst, state, null);
+ }
+
+ /**
+ * Maps the virtual connect point to a physical connect point.
+ *
+ * @param networkId network identifier
+ * @param virtualCp virtual connect point
+ * @return physical connect point
+ */
+ private ConnectPoint mapVirtualToPhysicalPort(NetworkId networkId,
+ ConnectPoint virtualCp) {
+ Set<VirtualPort> ports = store.getPorts(networkId, virtualCp.deviceId());
+ for (VirtualPort port : ports) {
+ if (port.number().equals(virtualCp.port())) {
+ return new ConnectPoint(port.realizedBy().deviceId(),
+ port.realizedBy().port());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Maps the physical connect point to a virtual connect point.
+ *
+ * @param networkId network identifier
+ * @param physicalCp physical connect point
+ * @return virtual connect point
+ */
+ private ConnectPoint mapPhysicalToVirtualToPort(NetworkId networkId,
+ ConnectPoint physicalCp) {
+ Set<VirtualPort> ports = store.getPorts(networkId, null);
+ for (VirtualPort port : ports) {
+ if (port.realizedBy().deviceId().equals(physicalCp.elementId()) &&
+ port.realizedBy().port().equals(physicalCp.port())) {
+ return new ConnectPoint(port.element().id(), port.number());
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void removeVirtualLink(NetworkId networkId, ConnectPoint src,
+ ConnectPoint dst) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(src, LINK_POINT_NULL);
+ checkNotNull(dst, LINK_POINT_NULL);
+ store.removeLink(networkId, src, dst);
+ }
+
+ @Override
+ public VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(portNumber, "Port description cannot be null");
+ return store.addPort(networkId, deviceId, portNumber, realizedBy);
+ }
+
+ @Override
+ public void bindVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(portNumber, "Port description cannot be null");
+ checkNotNull(realizedBy, "Physical port description cannot be null");
+
+ store.bindPort(networkId, deviceId, portNumber, realizedBy);
+ }
+
+ @Override
+ public void updatePortState(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, boolean isEnabled) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(portNumber, "Port description cannot be null");
+
+ store.updatePortState(networkId, deviceId, portNumber, isEnabled);
+ }
+
+ @Override
+ public void removeVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber) {
+ checkNotNull(networkId, NETWORK_NULL);
+ checkNotNull(deviceId, DEVICE_NULL);
+ checkNotNull(portNumber, "Port number cannot be null");
+ store.removePort(networkId, deviceId, portNumber);
+ }
+
+ @Override
+ public ServiceDirectory getServiceDirectory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
+ checkNotNull(tenantId, TENANT_NULL);
+ return store.getNetworks(tenantId);
+ }
+
+ @Override
+ public VirtualNetwork getVirtualNetwork(NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ return store.getNetwork(networkId);
+ }
+
+ @Override
+ public TenantId getTenantId(NetworkId networkId) {
+ VirtualNetwork virtualNetwork = getVirtualNetwork(networkId);
+ checkNotNull(virtualNetwork, "The network does not exist.");
+ return virtualNetwork.tenantId();
+ }
+
+ @Override
+ public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ return store.getDevices(networkId);
+ }
+
+ @Override
+ public Set<VirtualHost> getVirtualHosts(NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ return store.getHosts(networkId);
+ }
+
+ @Override
+ public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ return store.getLinks(networkId);
+ }
+
+ @Override
+ public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) {
+ checkNotNull(networkId, NETWORK_NULL);
+ return store.getPorts(networkId, deviceId);
+ }
+
+ @Override
+ public Set<DeviceId> getPhysicalDevices(NetworkId networkId, DeviceId deviceId) {
+ checkNotNull(networkId, "Network ID cannot be null");
+ checkNotNull(deviceId, "Virtual device ID cannot be null");
+ Set<VirtualPort> virtualPortSet = getVirtualPorts(networkId, deviceId);
+ Set<DeviceId> physicalDeviceSet = new HashSet<>();
+
+ virtualPortSet.forEach(virtualPort -> {
+ if (virtualPort.realizedBy() != null) {
+ physicalDeviceSet.add(virtualPort.realizedBy().deviceId());
+ }
+ });
+
+ return ImmutableSet.copyOf(physicalDeviceSet);
+ }
+
+ private final Map<ServiceKey, VnetService> networkServices = Maps.newConcurrentMap();
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T get(NetworkId networkId, Class<T> serviceClass) {
+ checkNotNull(networkId, NETWORK_NULL);
+ ServiceKey serviceKey = networkServiceKey(networkId, serviceClass);
+ VnetService service = lookup(serviceKey);
+ if (service == null) {
+ service = create(serviceKey);
+ }
+ return (T) service;
+ }
+
+ @Override
+ public ApplicationId getVirtualNetworkApplicationId(NetworkId networkId) {
+ return appId;
+ }
+
+ /**
+ * Returns the Vnet service matching the service key.
+ *
+ * @param serviceKey service key
+ * @return vnet service
+ */
+ private VnetService lookup(ServiceKey serviceKey) {
+ return networkServices.get(serviceKey);
+ }
+
+ /**
+ * Creates a new service key using the specified network identifier and service class.
+ *
+ * @param networkId network identifier
+ * @param serviceClass service class
+ * @param <T> type of service
+ * @return service key
+ */
+ private <T> ServiceKey networkServiceKey(NetworkId networkId, Class<T> serviceClass) {
+ return new ServiceKey(networkId, serviceClass);
+ }
+
+
+ /**
+ * Create a new vnet service instance.
+ *
+ * @param serviceKey service key
+ * @return vnet service
+ */
+ private VnetService create(ServiceKey serviceKey) {
+ VirtualNetwork network = getVirtualNetwork(serviceKey.networkId());
+ checkNotNull(network, NETWORK_NULL);
+
+ VnetService service;
+ if (serviceKey.serviceClass.equals(DeviceService.class)) {
+ service = new VirtualNetworkDeviceManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(LinkService.class)) {
+ service = new VirtualNetworkLinkManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(TopologyService.class)) {
+ service = new VirtualNetworkTopologyManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(IntentService.class)) {
+ service = new VirtualNetworkIntentManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(HostService.class)) {
+ service = new VirtualNetworkHostManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(PathService.class)) {
+ service = new VirtualNetworkPathManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(FlowRuleService.class)) {
+ service = new VirtualNetworkFlowRuleManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(PacketService.class)) {
+ service = new VirtualNetworkPacketManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(GroupService.class)) {
+ service = new VirtualNetworkGroupManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(MeterService.class)) {
+ service = new VirtualNetworkMeterManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(FlowObjectiveService.class)) {
+ service = new VirtualNetworkFlowObjectiveManager(this, network.id());
+ } else if (serviceKey.serviceClass.equals(MastershipService.class) ||
+ serviceKey.serviceClass.equals(MastershipAdminService.class) ||
+ serviceKey.serviceClass.equals(MastershipTermService.class)) {
+ service = new VirtualNetworkMastershipManager(this, network.id());
+ } else {
+ return null;
+ }
+ networkServices.put(serviceKey, service);
+ return service;
+ }
+
+ /**
+ * Service key class.
+ */
+ private static class ServiceKey {
+ final NetworkId networkId;
+ final Class serviceClass;
+
+ /**
+ * Constructor for service key.
+ *
+ * @param networkId network identifier
+ * @param serviceClass service class
+ */
+ ServiceKey(NetworkId networkId, Class serviceClass) {
+
+ checkNotNull(networkId, NETWORK_NULL);
+ this.networkId = networkId;
+ this.serviceClass = serviceClass;
+ }
+
+ /**
+ * Returns the network identifier.
+ *
+ * @return network identifier
+ */
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ /**
+ * Returns the service class.
+ *
+ * @return service class
+ */
+ public Class serviceClass() {
+ return serviceClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, serviceClass);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ServiceKey) {
+ ServiceKey that = (ServiceKey) obj;
+ return Objects.equals(this.networkId, that.networkId) &&
+ Objects.equals(this.serviceClass, that.serviceClass);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ protected VirtualNetworkProviderService
+ createProviderService(VirtualNetworkProvider provider) {
+ return new InternalVirtualNetworkProviderService(provider);
+ }
+
+ /**
+ * Service issued to registered virtual network providers so that they
+ * can interact with the core.
+ */
+ private class InternalVirtualNetworkProviderService
+ extends AbstractProviderService<VirtualNetworkProvider>
+ implements VirtualNetworkProviderService {
+ /**
+ * Constructor.
+ * @param provider virtual network provider
+ */
+ InternalVirtualNetworkProviderService(VirtualNetworkProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void topologyChanged(Set<Set<ConnectPoint>> clusters) {
+ Set<TenantId> tenantIds = getTenantIds();
+ tenantIds.forEach(tenantId -> {
+ Set<VirtualNetwork> virtualNetworks = getVirtualNetworks(tenantId);
+
+ virtualNetworks.forEach(virtualNetwork -> {
+ Set<VirtualLink> virtualLinks = getVirtualLinks(virtualNetwork.id());
+
+ virtualLinks.forEach(virtualLink -> {
+ if (isVirtualLinkInCluster(virtualNetwork.id(),
+ virtualLink, clusters)) {
+ store.updateLink(virtualLink, virtualLink.tunnelId(),
+ Link.State.ACTIVE);
+ } else {
+ store.updateLink(virtualLink, virtualLink.tunnelId(),
+ Link.State.INACTIVE);
+ }
+ });
+ });
+ });
+ }
+
+ /**
+ * Determines if the virtual link (both source and destination connect point)
+ * is in a cluster.
+ *
+ * @param networkId virtual network identifier
+ * @param virtualLink virtual link
+ * @param clusters topology clusters
+ * @return true if the virtual link is in a cluster.
+ */
+ private boolean isVirtualLinkInCluster(NetworkId networkId, VirtualLink virtualLink,
+ Set<Set<ConnectPoint>> clusters) {
+ ConnectPoint srcPhysicalCp =
+ mapVirtualToPhysicalPort(networkId, virtualLink.src());
+ ConnectPoint dstPhysicalCp =
+ mapVirtualToPhysicalPort(networkId, virtualLink.dst());
+
+ final boolean[] foundSrc = {false};
+ final boolean[] foundDst = {false};
+ clusters.forEach(connectPoints -> {
+ connectPoints.forEach(connectPoint -> {
+ if (connectPoint.equals(srcPhysicalCp)) {
+ foundSrc[0] = true;
+ } else if (connectPoint.equals(dstPhysicalCp)) {
+ foundDst[0] = true;
+ }
+ });
+ if (foundSrc[0] && foundDst[0]) {
+ return;
+ }
+ });
+ return foundSrc[0] && foundDst[0];
+ }
+
+ @Override
+ public void tunnelUp(NetworkId networkId, ConnectPoint src,
+ ConnectPoint dst, TunnelId tunnelId) {
+ ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
+ ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
+ if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
+ log.error("Src or dst virtual connection point was not found.");
+ }
+
+ VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
+ if (virtualLink != null) {
+ store.updateLink(virtualLink, tunnelId, Link.State.ACTIVE);
+ }
+ }
+
+ @Override
+ public void tunnelDown(NetworkId networkId, ConnectPoint src,
+ ConnectPoint dst, TunnelId tunnelId) {
+ ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
+ ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
+ if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
+ log.error("Src or dst virtual connection point was not found.");
+ }
+
+ VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
+ if (virtualLink != null) {
+ store.updateLink(virtualLink, tunnelId, Link.State.INACTIVE);
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java
new file mode 100644
index 0000000..57c0774
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import org.onlab.metrics.MetricsService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.core.MetricsHelper;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMastershipStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipInfo;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mastership.MastershipStoreDelegate;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.slf4j.Logger;
+import com.codahale.metrics.Timer;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.metrics.MetricsUtil.startTimer;
+import static org.onlab.metrics.MetricsUtil.stopTimer;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class VirtualNetworkMastershipManager
+ extends AbstractVirtualListenerManager<MastershipEvent, MastershipListener>
+ implements MastershipService, MastershipAdminService, MastershipTermService,
+ MetricsHelper {
+
+ private static final String NODE_ID_NULL = "Node ID cannot be null";
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String ROLE_NULL = "Mastership role cannot be null";
+
+ private final Logger log = getLogger(getClass());
+
+ protected ClusterService clusterService;
+
+ VirtualNetworkMastershipStore store;
+ MastershipStoreDelegate storeDelegate;
+
+ private NodeId localNodeId;
+ private Timer requestRoleTimer;
+
+ /**
+ * Creates a new VirtualNetworkMastershipManager object.
+ *
+ * @param manager virtual network manager service
+ * @param networkId virtual network identifier
+ */
+ public VirtualNetworkMastershipManager(VirtualNetworkService manager, NetworkId networkId) {
+ super(manager, networkId, MastershipEvent.class);
+
+ clusterService = serviceDirectory.get(ClusterService.class);
+
+ store = serviceDirectory.get(VirtualNetworkMastershipStore.class);
+ this.storeDelegate = new InternalDelegate();
+ store.setDelegate(networkId, this.storeDelegate);
+
+ requestRoleTimer = createTimer("Virtual-mastership", "requestRole", "responseTime");
+ localNodeId = clusterService.getLocalNode().id();
+ }
+
+ @Override
+ public CompletableFuture<Void> setRole(NodeId nodeId, DeviceId deviceId,
+ MastershipRole role) {
+ checkNotNull(nodeId, NODE_ID_NULL);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(role, ROLE_NULL);
+
+ CompletableFuture<MastershipEvent> eventFuture = null;
+
+ switch (role) {
+ case MASTER:
+ eventFuture = store.setMaster(networkId, nodeId, deviceId);
+ break;
+ case STANDBY:
+ eventFuture = store.setStandby(networkId, nodeId, deviceId);
+ break;
+ case NONE:
+ eventFuture = store.relinquishRole(networkId, nodeId, deviceId);
+ break;
+ default:
+ log.info("Unknown role; ignoring");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ return eventFuture.thenAccept(this::post).thenApply(v -> null);
+ }
+
+ @Override
+ public MastershipRole getLocalRole(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+
+ return store.getRole(networkId, localNodeId, deviceId);
+ }
+
+ @Override
+ public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+
+ final Timer.Context timer = startTimer(requestRoleTimer);
+ return store.requestRole(networkId, deviceId)
+ .whenComplete((result, error) -> stopTimer(timer));
+ }
+
+ @Override
+ public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
+ return store.relinquishRole(networkId, localNodeId, deviceId)
+ .thenAccept(this::post)
+ .thenApply(v -> null);
+ }
+
+ @Override
+ public NodeId getMasterFor(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+
+ return store.getMaster(networkId, deviceId);
+ }
+
+ @Override
+ public RoleInfo getNodesFor(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+
+ return store.getNodes(networkId, deviceId);
+ }
+
+ @Override
+ public MastershipInfo getMastershipFor(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getMastership(networkId, deviceId);
+ }
+
+ @Override
+ public Set<DeviceId> getDevicesOf(NodeId nodeId) {
+ checkNotNull(nodeId, NODE_ID_NULL);
+
+ return store.getDevices(networkId, nodeId);
+ }
+
+ @Override
+ public MastershipTerm getMastershipTerm(DeviceId deviceId) {
+ return store.getTermFor(networkId, deviceId);
+ }
+
+ @Override
+ public MetricsService metricsService() {
+ //TODO: support metric service for virtual network
+ log.warn("Currently, virtual network does not support metric service.");
+ return null;
+ }
+
+ @Override
+ public void balanceRoles() {
+ //FIXME: More advanced logic for balancing virtual network roles.
+ List<ControllerNode> nodes = clusterService.getNodes().stream()
+ .filter(n -> clusterService.getState(n.id())
+ .equals(ControllerNode.State.ACTIVE))
+ .collect(Collectors.toList());
+
+ nodes.sort(Comparator.comparing(ControllerNode::id));
+
+ //Pick a node using network Id,
+ NodeId masterNode = nodes.get((int) ((networkId.id() - 1) % nodes.size())).id();
+
+ List<CompletableFuture<Void>> setRoleFutures = Lists.newLinkedList();
+ for (VirtualDevice device : manager.getVirtualDevices(networkId)) {
+ setRoleFutures.add(setRole(masterNode, device.id(), MastershipRole.MASTER));
+ }
+
+ CompletableFuture<Void> balanceRolesFuture = CompletableFuture.allOf(
+ setRoleFutures.toArray(new CompletableFuture[setRoleFutures.size()]));
+
+ Futures.getUnchecked(balanceRolesFuture);
+ }
+
+ public class InternalDelegate implements MastershipStoreDelegate {
+ @Override
+ public void notify(MastershipEvent event) {
+ post(event);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
new file mode 100644
index 0000000..2cce14c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onlab.util.TriConsumer;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMeterStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterEvent;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterFeatures;
+import org.onosproject.net.meter.MeterFeaturesKey;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterKey;
+import org.onosproject.net.meter.MeterListener;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.meter.MeterState;
+import org.onosproject.net.meter.MeterStoreDelegate;
+import org.onosproject.net.meter.MeterStoreResult;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.AtomicCounter;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class VirtualNetworkMeterManager
+ extends AbstractVirtualListenerManager<MeterEvent, MeterListener>
+ implements MeterService {
+
+ private static final String METERCOUNTERIDENTIFIER = "meter-id-counter-%s";
+ private final Logger log = getLogger(getClass());
+
+ protected StorageService coreStorageService;
+
+ protected VirtualNetworkMeterStore store;
+ private final MeterStoreDelegate storeDelegate = new InternalMeterStoreDelegate();
+
+ private VirtualProviderRegistryService providerRegistryService;
+ private InternalMeterProviderService innerProviderService;
+
+ private Map<DeviceId, AtomicCounter> meterIdCounters
+ = Maps.newConcurrentMap();
+
+ private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
+
+ /**
+ * Creates a new VirtualNetworkMeterManager object.
+ *
+ * @param manager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkMeterManager(VirtualNetworkService manager,
+ NetworkId networkId) {
+ super(manager, networkId, MeterEvent.class);
+
+ coreStorageService = serviceDirectory.get(StorageService.class);
+ providerRegistryService =
+ serviceDirectory.get(VirtualProviderRegistryService.class);
+
+ store = serviceDirectory.get(VirtualNetworkMeterStore.class);
+ store.setDelegate(networkId, this.storeDelegate);
+
+ innerProviderService = new InternalMeterProviderService();
+ providerRegistryService.registerProviderService(networkId(), innerProviderService);
+
+
+ onComplete = (request, result, error) -> {
+ request.context().ifPresent(c -> {
+ if (error != null) {
+ c.onError(request, MeterFailReason.UNKNOWN);
+ } else {
+ if (result.reason().isPresent()) {
+ c.onError(request, result.reason().get());
+ } else {
+ c.onSuccess(request);
+ }
+ }
+ });
+
+ };
+
+ log.info("Started");
+ }
+
+ @Override
+ public Meter submit(MeterRequest request) {
+
+ MeterId id = allocateMeterId(request.deviceId());
+
+ Meter.Builder mBuilder = DefaultMeter.builder()
+ .forDevice(request.deviceId())
+ .fromApp(request.appId())
+ .withBands(request.bands())
+ .withId(id)
+ .withUnit(request.unit());
+
+ if (request.isBurst()) {
+ mBuilder.burst();
+ }
+ DefaultMeter m = (DefaultMeter) mBuilder.build();
+ m.setState(MeterState.PENDING_ADD);
+ store.storeMeter(networkId(), m).whenComplete((result, error) ->
+ onComplete.accept(request, result, error));
+ return m;
+ }
+
+ @Override
+ public void withdraw(MeterRequest request, MeterId meterId) {
+ Meter.Builder mBuilder = DefaultMeter.builder()
+ .forDevice(request.deviceId())
+ .fromApp(request.appId())
+ .withBands(request.bands())
+ .withId(meterId)
+ .withUnit(request.unit());
+
+ if (request.isBurst()) {
+ mBuilder.burst();
+ }
+
+ DefaultMeter m = (DefaultMeter) mBuilder.build();
+ m.setState(MeterState.PENDING_REMOVE);
+ store.deleteMeter(networkId(), m).whenComplete((result, error) ->
+ onComplete.accept(request, result, error));
+ }
+
+ @Override
+ public Meter getMeter(DeviceId deviceId, MeterId id) {
+ MeterKey key = MeterKey.key(deviceId, id);
+ return store.getMeter(networkId(), key);
+ }
+
+ @Override
+ public Collection<Meter> getMeters(DeviceId deviceId) {
+ return store.getAllMeters(networkId()).stream()
+ .filter(m -> m.deviceId().equals(deviceId)).collect(Collectors.toList());
+ }
+
+ @Override
+ public Collection<Meter> getAllMeters() {
+ return store.getAllMeters(networkId());
+ }
+
+ private long queryMeters(DeviceId device) {
+ //FIXME: how to decide maximum number of meters per virtual device?
+ return 1;
+ }
+
+ private AtomicCounter allocateCounter(DeviceId deviceId) {
+ return coreStorageService
+ .getAtomicCounter(String.format(METERCOUNTERIDENTIFIER, deviceId));
+ }
+
+ public MeterId allocateMeterId(DeviceId deviceId) {
+ long maxMeters = store.getMaxMeters(networkId(), MeterFeaturesKey.key(deviceId));
+ if (maxMeters == 0L) {
+ // MeterFeatures couldn't be retrieved, trying with queryMeters
+ maxMeters = queryMeters(deviceId);
+ }
+
+ if (maxMeters == 0L) {
+ throw new IllegalStateException("Meters not supported by device " + deviceId);
+ }
+
+ final long mmeters = maxMeters;
+ long id = meterIdCounters.compute(deviceId, (k, v) -> {
+ if (v == null) {
+ return allocateCounter(k);
+ }
+ if (v.get() >= mmeters) {
+ throw new IllegalStateException("Maximum number of meters " +
+ meterIdCounters.get(deviceId).get() +
+ " reached for device " + deviceId +
+ " virtual network " + networkId());
+ }
+ return v;
+ }).incrementAndGet();
+
+ return MeterId.meterId(id);
+ }
+
+ @Override
+ public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+ // Do nothing
+ }
+
+ private class InternalMeterProviderService
+ extends AbstractVirtualProviderService<VirtualMeterProvider>
+ implements VirtualMeterProviderService {
+
+ /**
+ * Creates a provider service on behalf of the specified provider.
+ */
+ protected InternalMeterProviderService() {
+ Set<ProviderId> providerIds =
+ providerRegistryService.getProvidersByService(this);
+ ProviderId providerId = providerIds.stream().findFirst().get();
+ VirtualMeterProvider provider = (VirtualMeterProvider)
+ providerRegistryService.getProvider(providerId);
+ setProvider(provider);
+ }
+
+ @Override
+ public void meterOperationFailed(MeterOperation operation,
+ MeterFailReason reason) {
+ store.failedMeter(networkId(), operation, reason);
+ }
+
+ @Override
+ public void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries) {
+ //FIXME: FOLLOWING CODE CANNOT BE TESTED UNTIL SOMETHING THAT
+ //FIXME: IMPLEMENTS METERS EXISTS
+ Map<Pair<DeviceId, MeterId>, Meter> storedMeterMap =
+ store.getAllMeters(networkId()).stream()
+ .collect(Collectors.toMap(m -> Pair.of(m.deviceId(), m.id()), Function.identity()));
+
+ meterEntries.stream()
+ .filter(m -> storedMeterMap.remove(Pair.of(m.deviceId(), m.id())) != null)
+ .forEach(m -> store.updateMeterState(networkId(), m));
+
+ storedMeterMap.values().forEach(m -> {
+ if (m.state() == MeterState.PENDING_ADD) {
+ provider().performMeterOperation(networkId(), m.deviceId(),
+ new MeterOperation(m,
+ MeterOperation.Type.MODIFY));
+ } else if (m.state() == MeterState.PENDING_REMOVE) {
+ store.deleteMeterNow(networkId(), m);
+ }
+ });
+ }
+
+ @Override
+ public void pushMeterFeatures(DeviceId deviceId, MeterFeatures meterfeatures) {
+ store.storeMeterFeatures(networkId(), meterfeatures);
+ }
+
+ @Override
+ public void deleteMeterFeatures(DeviceId deviceId) {
+ store.deleteMeterFeatures(networkId(), deviceId);
+ }
+ }
+
+ private class InternalMeterStoreDelegate implements MeterStoreDelegate {
+
+ @Override
+ public void notify(MeterEvent event) {
+ DeviceId deviceId = event.subject().deviceId();
+ VirtualMeterProvider p = innerProviderService.provider();
+
+ switch (event.type()) {
+ case METER_ADD_REQ:
+ p.performMeterOperation(networkId(), deviceId,
+ new MeterOperation(event.subject(),
+ MeterOperation.Type.ADD));
+ break;
+ case METER_REM_REQ:
+ p.performMeterOperation(networkId(), deviceId,
+ new MeterOperation(event.subject(),
+ MeterOperation.Type.REMOVE));
+ break;
+ default:
+ log.warn("Unknown meter event {}", event.type());
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManager.java
new file mode 100644
index 0000000..d1869ce
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManager.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2018-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.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.virtual.AbstractVnetService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.packet.DefaultPacketRequest;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketProcessorEntry;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.packet.PacketStoreDelegate;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public class VirtualNetworkPacketManager extends AbstractVnetService
+ implements PacketService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final VirtualNetworkService manager;
+
+ protected VirtualNetworkPacketStore store;
+ private final List<ProcessorEntry> processors = Lists.newCopyOnWriteArrayList();
+
+ private NodeId localNodeId;
+
+ private DeviceService deviceService;
+ private FlowObjectiveService objectiveService;
+
+ private VirtualProviderRegistryService providerRegistryService = null;
+
+ private InternalPacketProviderService providerService = null;
+
+ public VirtualNetworkPacketManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId);
+ this.manager = virtualNetworkManager;
+
+ //Set node id as same as the node hosting virtual manager
+ ClusterService clusterService = serviceDirectory.get(ClusterService.class);
+ this.localNodeId = clusterService.getLocalNode().id();
+
+ this.store = serviceDirectory.get(VirtualNetworkPacketStore.class);
+ this.store.setDelegate(networkId(), new InternalStoreDelegate());
+
+ this.deviceService = manager.get(networkId(), DeviceService.class);
+ this.objectiveService = manager.get(networkId(), FlowObjectiveService.class);
+
+ providerRegistryService =
+ serviceDirectory.get(VirtualProviderRegistryService.class);
+ providerService = new InternalPacketProviderService();
+ providerRegistryService.registerProviderService(networkId(), providerService);
+ }
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ ProcessorEntry entry = new ProcessorEntry(processor, priority);
+
+ // Insert the new processor according to its priority.
+ int i = 0;
+ for (; i < processors.size(); i++) {
+ if (priority < processors.get(i).priority()) {
+ break;
+ }
+ }
+ processors.add(i, entry);
+ }
+
+ @Override
+ public void removeProcessor(PacketProcessor processor) {
+ // Remove the processor entry.
+ for (int i = 0; i < processors.size(); i++) {
+ if (processors.get(i).processor() == processor) {
+ processors.remove(i);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public List<PacketProcessorEntry> getProcessors() {
+ return ImmutableList.copyOf(processors);
+ }
+
+ @Override
+ public void requestPackets(TrafficSelector selector, PacketPriority priority, ApplicationId appId) {
+ PacketRequest request = new DefaultPacketRequest(selector, priority, appId,
+ localNodeId, Optional.empty());
+ store.requestPackets(networkId(), request);
+ }
+
+ @Override
+ public void requestPackets(TrafficSelector selector, PacketPriority priority,
+ ApplicationId appId, Optional<DeviceId> deviceId) {
+ PacketRequest request =
+ new DefaultPacketRequest(selector, priority, appId,
+ localNodeId, deviceId);
+
+ store.requestPackets(networkId(), request);
+ }
+
+ @Override
+ public void cancelPackets(TrafficSelector selector, PacketPriority priority, ApplicationId appId) {
+ PacketRequest request = new DefaultPacketRequest(selector, priority, appId,
+ localNodeId, Optional.empty());
+ store.cancelPackets(networkId(), request);
+ }
+
+ @Override
+ public void cancelPackets(TrafficSelector selector, PacketPriority priority,
+ ApplicationId appId, Optional<DeviceId> deviceId) {
+ PacketRequest request = new DefaultPacketRequest(selector, priority,
+ appId, localNodeId,
+ deviceId);
+ store.cancelPackets(networkId(), request);
+ }
+
+ @Override
+ public List<PacketRequest> getRequests() {
+ return store.existingRequests(networkId());
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ store.emit(networkId(), packet);
+ }
+
+ /**
+ * Personalized packet provider service issued to the supplied provider.
+ */
+ private class InternalPacketProviderService
+ extends AbstractVirtualProviderService<VirtualPacketProvider>
+ implements VirtualPacketProviderService {
+
+ protected InternalPacketProviderService() {
+ super();
+
+ Set<ProviderId> providerIds =
+ providerRegistryService.getProvidersByService(this);
+ ProviderId providerId = providerIds.stream().findFirst().get();
+ VirtualPacketProvider provider = (VirtualPacketProvider)
+ providerRegistryService.getProvider(providerId);
+ setProvider(provider);
+ }
+
+ @Override
+ public void processPacket(PacketContext context) {
+ // TODO filter packets sent to processors based on registrations
+ for (ProcessorEntry entry : processors) {
+ try {
+ long start = System.nanoTime();
+ entry.processor().process(context);
+ entry.addNanos(System.nanoTime() - start);
+ } catch (Exception e) {
+ log.warn("Packet processor {} threw an exception", entry.processor(), e);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Entity for tracking stats for a packet processor.
+ */
+ private class ProcessorEntry implements PacketProcessorEntry {
+ private final PacketProcessor processor;
+ private final int priority;
+ private long invocations = 0;
+ private long nanos = 0;
+
+ public ProcessorEntry(PacketProcessor processor, int priority) {
+ this.processor = processor;
+ this.priority = priority;
+ }
+
+ @Override
+ public PacketProcessor processor() {
+ return processor;
+ }
+
+ @Override
+ public int priority() {
+ return priority;
+ }
+
+ @Override
+ public long invocations() {
+ return invocations;
+ }
+
+ @Override
+ public long totalNanos() {
+ return nanos;
+ }
+
+ @Override
+ public long averageNanos() {
+ return invocations > 0 ? nanos / invocations : 0;
+ }
+
+ void addNanos(long nanos) {
+ this.nanos += nanos;
+ this.invocations++;
+ }
+ }
+
+ private void localEmit(NetworkId networkId, OutboundPacket packet) {
+ Device device = deviceService.getDevice(packet.sendThrough());
+ if (device == null) {
+ return;
+ }
+ VirtualPacketProvider packetProvider = providerService.provider();
+
+ if (packetProvider != null) {
+ packetProvider.emit(networkId, packet);
+ }
+ }
+
+ /**
+ * Internal callback from the packet store.
+ */
+ protected class InternalStoreDelegate implements PacketStoreDelegate {
+ @Override
+ public void notify(PacketEvent event) {
+ localEmit(networkId(), event.subject());
+ }
+
+ @Override
+ public void requestPackets(PacketRequest request) {
+ DeviceId deviceid = request.deviceId().orElse(null);
+
+ if (deviceid != null) {
+ pushRule(deviceService.getDevice(deviceid), request);
+ } else {
+ pushToAllDevices(request);
+ }
+ }
+
+ @Override
+ public void cancelPackets(PacketRequest request) {
+ DeviceId deviceid = request.deviceId().orElse(null);
+
+ if (deviceid != null) {
+ removeRule(deviceService.getDevice(deviceid), request);
+ } else {
+ removeFromAllDevices(request);
+ }
+ }
+ }
+
+ /**
+ * Pushes packet intercept flow rules to the device.
+ *
+ * @param device the device to push the rules to
+ * @param request the packet request
+ */
+ private void pushRule(Device device, PacketRequest request) {
+ if (!device.type().equals(Device.Type.VIRTUAL)) {
+ return;
+ }
+
+ ForwardingObjective forwarding = createBuilder(request)
+ .add(new ObjectiveContext() {
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to install packet request {} to {}: {}",
+ request, device.id(), error);
+ }
+ });
+
+ objectiveService.forward(device.id(), forwarding);
+ }
+
+ /**
+ * Removes packet intercept flow rules from the device.
+ *
+ * @param device the device to remove the rules deom
+ * @param request the packet request
+ */
+ private void removeRule(Device device, PacketRequest request) {
+ if (!device.type().equals(Device.Type.VIRTUAL)) {
+ return;
+ }
+ ForwardingObjective forwarding = createBuilder(request)
+ .remove(new ObjectiveContext() {
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to withdraw packet request {} from {}: {}",
+ request, device.id(), error);
+ }
+ });
+ objectiveService.forward(device.id(), forwarding);
+ }
+
+ /**
+ * Pushes a packet request flow rule to all devices.
+ *
+ * @param request the packet request
+ */
+ private void pushToAllDevices(PacketRequest request) {
+ log.debug("Pushing packet request {} to all devices", request);
+ for (Device device : deviceService.getDevices()) {
+ pushRule(device, request);
+ }
+ }
+
+ /**
+ * Removes packet request flow rule from all devices.
+ *
+ * @param request the packet request
+ */
+ private void removeFromAllDevices(PacketRequest request) {
+ deviceService.getAvailableDevices().forEach(d -> removeRule(d, request));
+ }
+
+ private DefaultForwardingObjective.Builder createBuilder(PacketRequest request) {
+ return DefaultForwardingObjective.builder()
+ .withPriority(request.priority().priorityValue())
+ .withSelector(request.selector())
+ .fromApp(request.appId())
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withTreatment(DefaultTrafficTreatment.builder().punt().build())
+ .makePermanent();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManager.java
new file mode 100644
index 0000000..de0dffa
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManager.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VnetService;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.AbstractPathService;
+import org.onosproject.net.topology.TopologyService;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Path service implementation built on the virtual network service.
+ */
+public class VirtualNetworkPathManager
+ extends AbstractPathService
+ implements PathService, VnetService {
+
+ private final NetworkId networkId;
+
+ /**
+ * Creates a new virtual network path service object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+
+ public VirtualNetworkPathManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ this.networkId = networkId;
+
+ topologyService = virtualNetworkManager.get(networkId(), TopologyService.class);
+ hostService = virtualNetworkManager.get(networkId(), HostService.class);
+ }
+
+ @Override
+ public Set<Path> getPaths(ElementId src, ElementId dst) {
+ return super.getPaths(src, dst, (LinkWeigher) null);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst) {
+ return getDisjointPaths(src, dst, (LinkWeigher) null);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+ Map<Link, Object> riskProfile) {
+ return getDisjointPaths(src, dst, (LinkWeigher) null, riskProfile);
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return this.networkId;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManager.java
new file mode 100644
index 0000000..aad3ac4
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2018-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.impl;
+
+import org.onosproject.common.DefaultTopology;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.topology.ClusterId;
+import org.onosproject.net.topology.DefaultGraphDescription;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyCluster;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyService;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.incubator.net.virtual.DefaultVirtualLink.PID;
+
+/**
+ * Topology service implementation built on the virtual network service.
+ */
+public class VirtualNetworkTopologyManager
+ extends AbstractVirtualListenerManager<TopologyEvent, TopologyListener>
+ implements TopologyService {
+
+ private static final String TOPOLOGY_NULL = "Topology cannot be null";
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null";
+ private static final String CLUSTER_NULL = "Topology cluster cannot be null";
+ private static final String CONNECTION_POINT_NULL = "Connection point cannot be null";
+ private static final String LINK_WEIGHT_NULL = "Link weight cannot be null";
+
+ /**
+ * Creates a new VirtualNetworkTopologyService object.
+ *
+ * @param virtualNetworkManager virtual network manager service
+ * @param networkId a virtual network identifier
+ */
+ public VirtualNetworkTopologyManager(VirtualNetworkService virtualNetworkManager,
+ NetworkId networkId) {
+ super(virtualNetworkManager, networkId, TopologyEvent.class);
+ }
+
+ @Override
+ public Topology currentTopology() {
+ Iterable<Device> devices = manager.getVirtualDevices(networkId())
+ .stream()
+ .collect(Collectors.toSet());
+ Iterable<Link> links = manager.getVirtualLinks(networkId())
+ .stream()
+ .collect(Collectors.toSet());
+
+ DefaultGraphDescription graph =
+ new DefaultGraphDescription(System.nanoTime(),
+ System.currentTimeMillis(),
+ devices, links);
+ return new DefaultTopology(PID, graph);
+ }
+
+ @Override
+ public boolean isLatest(Topology topology) {
+ Topology currentTopology = currentTopology();
+ return defaultTopology(topology).getGraph()
+ .equals(defaultTopology(currentTopology).getGraph());
+ }
+
+ @Override
+ public TopologyGraph getGraph(Topology topology) {
+ return defaultTopology(topology).getGraph();
+ }
+
+ // Validates the specified topology and returns it as a default
+ private DefaultTopology defaultTopology(Topology topology) {
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkArgument(topology instanceof DefaultTopology,
+ "Topology class %s not supported", topology.getClass());
+ return (DefaultTopology) topology;
+ }
+
+ @Override
+ public Set<TopologyCluster> getClusters(Topology topology) {
+ return defaultTopology(topology).getClusters();
+ }
+
+ @Override
+ public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
+ checkNotNull(clusterId, CLUSTER_ID_NULL);
+ return defaultTopology(topology).getCluster(clusterId);
+ }
+
+ @Override
+ public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
+ checkNotNull(cluster, CLUSTER_NULL);
+ return defaultTopology(topology).getClusterDevices(cluster);
+ }
+
+ @Override
+ public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
+ checkNotNull(cluster, CLUSTER_NULL);
+ return defaultTopology(topology).getClusterLinks(cluster);
+ }
+
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ return defaultTopology(topology).getPaths(src, dst);
+ }
+
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+ LinkWeigher weigher) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ checkNotNull(weigher, LINK_WEIGHT_NULL);
+ return defaultTopology(topology).getPaths(src, dst, weigher);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+ DeviceId dst) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ return defaultTopology(topology).getDisjointPaths(src, dst);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+ DeviceId dst,
+ LinkWeigher weigher) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ checkNotNull(weigher, LINK_WEIGHT_NULL);
+ return defaultTopology(topology).getDisjointPaths(src, dst, weigher);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+ DeviceId dst,
+ Map<Link, Object> riskProfile) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
+ }
+
+ @Override
+ public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+ DeviceId dst,
+ LinkWeigher weigher,
+ Map<Link, Object> riskProfile) {
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ checkNotNull(weigher, LINK_WEIGHT_NULL);
+ return defaultTopology(topology).getDisjointPaths(src, dst, weigher,
+ riskProfile);
+ }
+
+ @Override
+ public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CONNECTION_POINT_NULL);
+ return defaultTopology(topology).isInfrastructure(connectPoint);
+ }
+
+ @Override
+ public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, CONNECTION_POINT_NULL);
+ return defaultTopology(topology).isBroadcastPoint(connectPoint);
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java
new file mode 100644
index 0000000..0328383
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java
@@ -0,0 +1,82 @@
+/*
+ * 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.impl.intent;
+
+import com.google.common.collect.Maps;
+import org.onlab.util.AbstractAccumulator;
+import org.onosproject.net.intent.IntentBatchDelegate;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.Key;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+
+/**
+ * An accumulator for building batches of intent operations for virtual network.
+ * Only one batch should be in process per instance at a time.
+ */
+public class VirtualIntentAccumulator extends AbstractAccumulator<IntentData> {
+ private static final int DEFAULT_MAX_EVENTS = 1000;
+ private static final int DEFAULT_MAX_IDLE_MS = 10;
+ private static final int DEFAULT_MAX_BATCH_MS = 50;
+
+ // FIXME: Replace with a system-wide timer instance;
+ // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
+ private static final Timer TIMER = new Timer("virtual-intent-op-batching");
+
+ private final IntentBatchDelegate delegate;
+
+ private volatile boolean ready;
+
+ /**
+ * Creates an intent operation accumulator.
+ *
+ * @param delegate the intent batch delegate
+ */
+ public VirtualIntentAccumulator(IntentBatchDelegate delegate) {
+ super(TIMER, DEFAULT_MAX_EVENTS, DEFAULT_MAX_BATCH_MS, DEFAULT_MAX_IDLE_MS);
+ this.delegate = delegate;
+ // Assume that the delegate is ready for work at the start
+ ready = true; //TODO validate the assumption that delegate is ready
+ }
+
+ @Override
+ public void processItems(List<IntentData> items) {
+ ready = false;
+ delegate.execute(reduce(items));
+ }
+
+ private Collection<IntentData> reduce(List<IntentData> ops) {
+ Map<Key, IntentData> map = Maps.newHashMap();
+ for (IntentData op : ops) {
+ map.put(op.key(), op);
+ }
+ //TODO check the version... or maybe store will handle this.
+ return map.values();
+ }
+
+ @Override
+ public boolean isReady() {
+ return ready;
+ }
+
+ public void ready() {
+ ready = true;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java
new file mode 100644
index 0000000..f922c22
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java
@@ -0,0 +1,170 @@
+/*
+ * 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.impl.intent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.intent.VirtualIntentCompiler;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentException;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public final class VirtualIntentCompilerRegistry {
+ private final ConcurrentMap<Class<? extends Intent>,
+ VirtualIntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+
+ // non-instantiable (except for our Singleton)
+ private VirtualIntentCompilerRegistry() {
+
+ }
+
+ public static VirtualIntentCompilerRegistry getInstance() {
+ return SingletonHelper.INSTANCE;
+ }
+
+ /**
+ * Registers the specified compiler for the given intent class.
+ *
+ * @param cls intent class
+ * @param compiler intent compiler
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void registerCompiler(Class<T> cls,
+ VirtualIntentCompiler<T> compiler) {
+ compilers.put(cls, compiler);
+ }
+
+ /**
+ * Unregisters the compiler for the specified intent class.
+ *
+ * @param cls intent class
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+ compilers.remove(cls);
+ }
+
+ /**
+ * Returns immutable set of bindings of currently registered intent compilers.
+ *
+ * @return the set of compiler bindings
+ */
+ public Map<Class<? extends Intent>, VirtualIntentCompiler<? extends Intent>> getCompilers() {
+ return ImmutableMap.copyOf(compilers);
+ }
+
+ /**
+ * Compiles an intent recursively.
+ *
+ * @param networkId network identifier
+ * @param intent intent
+ * @param previousInstallables previous intent installables
+ * @return result of compilation
+ */
+ public List<Intent> compile(NetworkId networkId,
+ Intent intent, List<Intent> previousInstallables) {
+ if (intent.isInstallable()) {
+ return ImmutableList.of(intent);
+ }
+
+ // FIXME: get previous resources
+ List<Intent> installables = new ArrayList<>();
+ Queue<Intent> compileQueue = new LinkedList<>();
+ compileQueue.add(intent);
+
+ Intent compiling;
+ while ((compiling = compileQueue.poll()) != null) {
+ registerSubclassCompilerIfNeeded(compiling);
+
+ List<Intent> compiled = getCompiler(compiling)
+ .compile(networkId, compiling, previousInstallables);
+
+ compiled.forEach(i -> {
+ if (i.isInstallable()) {
+ installables.add(i);
+ } else {
+ compileQueue.add(i);
+ }
+ });
+ }
+ return installables;
+ }
+
+ /**
+ * Returns the corresponding intent compiler to the specified intent.
+ *
+ * @param intent intent
+ * @param <T> the type of intent
+ * @return intent compiler corresponding to the specified intent
+ */
+ private <T extends Intent> VirtualIntentCompiler<T> getCompiler(T intent) {
+ @SuppressWarnings("unchecked")
+ VirtualIntentCompiler<T> compiler =
+ (VirtualIntentCompiler<T>) compilers.get(intent.getClass());
+ if (compiler == null) {
+ throw new IntentException("no compiler for class " + intent.getClass());
+ }
+ return compiler;
+ }
+
+ /**
+ * Registers an intent compiler of the specified intent if an intent compiler
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent compiler for a parent type is found, this method
+ * registers the found intent compiler.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassCompilerIfNeeded(Intent intent) {
+ if (!compilers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the Intent class descendants
+ if (Intent.class.isAssignableFrom(cls)) {
+ VirtualIntentCompiler<?> compiler = compilers.get(cls);
+ if (compiler != null) {
+ compilers.put(intent.getClass(), compiler);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+
+ /**
+ * Prevents object instantiation from external.
+ */
+ private static final class SingletonHelper {
+ private static final String ILLEGAL_ACCESS_MSG =
+ "Should not instantiate this class.";
+ private static final VirtualIntentCompilerRegistry INSTANCE =
+ new VirtualIntentCompilerRegistry();
+
+ private SingletonHelper() {
+ throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java
new file mode 100644
index 0000000..4a09250
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java
@@ -0,0 +1,226 @@
+/*
+ * 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.impl.intent;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.incubator.net.virtual.impl.VirtualNetworkIntentManager;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onosproject.net.intent.IntentState.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of IntentInstallCoordinator for virtual network.
+ */
+public class VirtualIntentInstallCoordinator {
+ private static final String INSTALLER_NOT_FOUND = "Intent installer not found, Intent: {}";
+ private final Logger log = getLogger(VirtualNetworkIntentManager.class);
+
+ NetworkId networkId;
+ private VirtualIntentInstallerRegistry installerRegistry;
+ private VirtualNetworkIntentStore intentStore;
+
+ /**
+ * Creates an InstallCoordinator.
+ *
+ * @param networkId virtual network identifier
+ * @param installerRegistry the installer registry
+ * @param intentStore the Intent store
+ */
+ public VirtualIntentInstallCoordinator(NetworkId networkId,
+ VirtualIntentInstallerRegistry installerRegistry,
+ VirtualNetworkIntentStore intentStore) {
+ this.networkId = networkId;
+ this.installerRegistry = installerRegistry;
+ this.intentStore = intentStore;
+ }
+
+ /**
+ * Applies Intent data to be uninstalled and to be installed.
+ *
+ * @param toUninstall Intent data to be uninstalled
+ * @param toInstall Intent data to be installed
+ */
+ public void installIntents(Optional<IntentData> toUninstall,
+ Optional<IntentData> toInstall) {
+ // If no any Intents to be uninstalled or installed, ignore it.
+ if (!toUninstall.isPresent() && !toInstall.isPresent()) {
+ return;
+ }
+
+ // Classify installable Intents to different installers.
+ ArrayListMultimap<IntentInstaller, Intent> uninstallInstallers;
+ ArrayListMultimap<IntentInstaller, Intent> installInstallers;
+ Set<IntentInstaller> allInstallers = Sets.newHashSet();
+
+ if (toUninstall.isPresent()) {
+ uninstallInstallers = getInstallers(toUninstall.get());
+ allInstallers.addAll(uninstallInstallers.keySet());
+ } else {
+ uninstallInstallers = ArrayListMultimap.create();
+ }
+
+ if (toInstall.isPresent()) {
+ installInstallers = getInstallers(toInstall.get());
+ allInstallers.addAll(installInstallers.keySet());
+ } else {
+ installInstallers = ArrayListMultimap.create();
+ }
+
+ // Generates an installation context for the high level Intent.
+ IntentInstallationContext installationContext =
+ new IntentInstallationContext(toUninstall.orElse(null), toInstall.orElse(null));
+
+ //Generates different operation context for different installable Intents.
+ Map<IntentInstaller, IntentOperationContext> contexts = Maps.newHashMap();
+ allInstallers.forEach(installer -> {
+ List<Intent> intentsToUninstall = uninstallInstallers.get(installer);
+ List<Intent> intentsToInstall = installInstallers.get(installer);
+
+ // Connect context to high level installation context
+ IntentOperationContext context =
+ new IntentOperationContext(intentsToUninstall, intentsToInstall,
+ installationContext);
+ installationContext.addPendingContext(context);
+ contexts.put(installer, context);
+ });
+
+ // Apply contexts to installers
+ contexts.forEach((installer, context) -> {
+ installer.apply(context);
+ });
+ }
+
+ /**
+ * Generates a mapping for installable Intents to installers.
+ *
+ * @param intentData the Intent data which contains installable Intents
+ * @return the mapping for installable Intents to installers
+ */
+ private ArrayListMultimap<IntentInstaller, Intent> getInstallers(IntentData intentData) {
+ ArrayListMultimap<IntentInstaller, Intent> intentInstallers = ArrayListMultimap.create();
+ intentData.installables().forEach(intent -> {
+ IntentInstaller installer = installerRegistry.getInstaller(intent.getClass());
+ if (installer != null) {
+ intentInstallers.put(installer, intent);
+ } else {
+ log.warn(INSTALLER_NOT_FOUND, intent);
+ }
+ });
+ return intentInstallers;
+ }
+
+ /**
+ * Handles success operation context.
+ *
+ * @param context the operation context
+ */
+ public void success(IntentOperationContext context) {
+ IntentInstallationContext intentInstallationContext =
+ context.intentInstallationContext();
+ intentInstallationContext.removePendingContext(context);
+
+ if (intentInstallationContext.isPendingContextsEmpty()) {
+ finish(intentInstallationContext);
+ }
+ }
+
+ /**
+ * Handles failed operation context.
+ *
+ * @param context the operation context
+ */
+ public void failed(IntentOperationContext context) {
+ IntentInstallationContext intentInstallationContext =
+ context.intentInstallationContext();
+ intentInstallationContext.addErrorContext(context);
+ intentInstallationContext.removePendingContext(context);
+
+ if (intentInstallationContext.isPendingContextsEmpty()) {
+ finish(intentInstallationContext);
+ }
+ }
+
+ /**
+ * Completed the installation context and update the Intent store.
+ *
+ * @param intentInstallationContext the installation context
+ */
+ private void finish(IntentInstallationContext intentInstallationContext) {
+ Set<IntentOperationContext> errCtxs = intentInstallationContext.errorContexts();
+ Optional<IntentData> toUninstall = intentInstallationContext.toUninstall();
+ Optional<IntentData> toInstall = intentInstallationContext.toInstall();
+
+ // Intent install success
+ if (errCtxs == null || errCtxs.isEmpty()) {
+ if (toInstall.isPresent()) {
+ IntentData installData = toInstall.get();
+ log.debug("Completed installing: {}", installData.key());
+ installData = new IntentData(installData, installData.installables());
+ installData.setState(INSTALLED);
+ intentStore.write(networkId, installData);
+ } else if (toUninstall.isPresent()) {
+ IntentData uninstallData = toUninstall.get();
+ uninstallData = new IntentData(uninstallData, Collections.emptyList());
+ log.debug("Completed withdrawing: {}", uninstallData.key());
+ switch (uninstallData.request()) {
+ case INSTALL_REQ:
+ log.warn("{} was requested to withdraw during installation?",
+ uninstallData.intent());
+ uninstallData.setState(FAILED);
+ break;
+ case WITHDRAW_REQ:
+ default: //TODO "default" case should not happen
+ uninstallData.setState(WITHDRAWN);
+ break;
+ }
+ // Intent has been withdrawn; we can clear the installables
+ intentStore.write(networkId, uninstallData);
+ }
+ } else {
+ // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
+ if (toInstall.isPresent()) {
+ IntentData installData = toInstall.get();
+ installData.setState(CORRUPT);
+ installData.incrementErrorCount();
+ intentStore.write(networkId, installData);
+ }
+ // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
+ if (toUninstall.isPresent()) {
+ IntentData uninstallData = toUninstall.get();
+ uninstallData.setState(CORRUPT);
+ uninstallData.incrementErrorCount();
+ intentStore.write(networkId, uninstallData);
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java
new file mode 100644
index 0000000..a0e6448
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java
@@ -0,0 +1,96 @@
+/*
+ * 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.impl.intent;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentInstaller;
+
+import java.util.Map;
+
+/**
+ * The local registry for Intent installer for virtual networks.
+ */
+public final class VirtualIntentInstallerRegistry {
+ private final Map<Class<? extends Intent>,
+ IntentInstaller<? extends Intent>> installers;
+
+ // non-instantiable (except for our Singleton)
+ private VirtualIntentInstallerRegistry() {
+ installers = Maps.newConcurrentMap();
+ }
+
+ public static VirtualIntentInstallerRegistry getInstance() {
+ return SingletonHelper.INSTANCE;
+ }
+
+ /**
+ * Registers the specific installer for the given intent class.
+ *
+ * @param cls intent class
+ * @param installer intent installer
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+ installers.put(cls, installer);
+ }
+
+ /**
+ * Unregisters the installer for the specific intent class.
+ *
+ * @param cls intent class
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+ installers.remove(cls);
+ }
+
+ /**
+ * Returns immutable set of binding of currently registered intent installers.
+ *
+ * @return the set of installer bindings
+ */
+ public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+ return ImmutableMap.copyOf(installers);
+ }
+
+ /**
+ * Get an Intent installer by given Intent type.
+ *
+ * @param cls the Intent type
+ * @param <T> the Intent type
+ * @return the Intent installer of the Intent type if exists; null otherwise
+ */
+ public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+ return (IntentInstaller<T>) installers.get(cls);
+ }
+
+ /**
+ * Prevents object instantiation from external.
+ */
+ private static final class SingletonHelper {
+ private static final String ILLEGAL_ACCESS_MSG =
+ "Should not instantiate this class.";
+ private static final VirtualIntentInstallerRegistry INSTANCE =
+ new VirtualIntentInstallerRegistry();
+
+ private SingletonHelper() {
+ throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java
new file mode 100644
index 0000000..a7719f9
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.impl.intent;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * A collection of methods to process an intent for virtual networks.
+ *
+ * This interface is public, but intended to be used only by IntentManager and
+ * IntentProcessPhase subclasses stored under phase package.
+ */
+public interface VirtualIntentProcessor {
+ /**
+ * Compiles an intent recursively.
+ *
+ * @param networkId virtual network identifier
+ * @param intent intent
+ * @param previousInstallables previous intent installables
+ * @return result of compilation
+ */
+ List<Intent> compile(NetworkId networkId, Intent intent, List<Intent> previousInstallables);
+
+ /**
+ * Applies intents.
+ *
+ * @param networkId virtual network identifier
+ * @param toUninstall Intent data describing flows to uninstall.
+ * @param toInstall Intent data describing flows to install.
+ */
+ void apply(NetworkId networkId, Optional<IntentData> toUninstall,
+ Optional<IntentData> toInstall);
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java
new file mode 100644
index 0000000..df7f451
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java
@@ -0,0 +1,48 @@
+/*
+ * 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.impl.intent;
+
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualFinalIntentProcessPhase;
+import org.onosproject.net.intent.IntentData;
+
+/**
+ * Represents a phase where an intent is not compiled for a virtual network.
+ * This should be used if a new version of the intent will immediately override
+ * this one.
+ */
+public final class VirtualIntentSkipped extends VirtualFinalIntentProcessPhase {
+
+ private static final VirtualIntentSkipped SINGLETON = new VirtualIntentSkipped();
+
+ /**
+ * Returns a shared skipped phase.
+ *
+ * @return skipped phase
+ */
+ public static VirtualIntentSkipped getPhase() {
+ return SINGLETON;
+ }
+
+ // Prevent object construction; use getPhase()
+ private VirtualIntentSkipped() {
+ }
+
+ @Override
+ public IntentData data() {
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java
new file mode 100644
index 0000000..c6758ae
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.impl.intent;
\ No newline at end of file
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java
new file mode 100644
index 0000000..978c95c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java
@@ -0,0 +1,46 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+/**
+ * Represents a final phase of processing an intent for virtual networks.
+ */
+public abstract class VirtualFinalIntentProcessPhase
+ implements VirtualIntentProcessPhase {
+
+ @Override
+ public final Optional<VirtualIntentProcessPhase> execute() {
+ preExecute();
+ return Optional.empty();
+ }
+
+ /**
+ * Executes operations that must take place before the phase starts.
+ */
+ protected void preExecute() {}
+
+ /**
+ * Returns the IntentData object being acted on by this phase.
+ *
+ * @return intent data object for the phase
+ */
+ public abstract IntentData data();
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java
new file mode 100644
index 0000000..639c04c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java
@@ -0,0 +1,81 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Represents a phase where an intent is being compiled or recompiled
+ * for virtual networks.
+ */
+public class VirtualIntentCompiling implements VirtualIntentProcessPhase {
+ private static final Logger log = LoggerFactory.getLogger(VirtualIntentCompiling.class);
+
+ private final NetworkId networkId;
+ private final VirtualIntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates a intent recompiling phase.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor that does work for recompiling
+ * @param data intent data containing an intent to be recompiled
+ * @param stored intent data stored in the store
+ */
+ VirtualIntentCompiling(NetworkId networkId, VirtualIntentProcessor processor,
+ IntentData data, Optional<IntentData> stored) {
+ this.networkId = checkNotNull(networkId);
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<VirtualIntentProcessPhase> execute() {
+ try {
+ List<Intent> compiled = processor
+ .compile(networkId, data.intent(),
+ //TODO consider passing an optional here in the future
+ stored.map(IntentData::installables).orElse(null));
+ return Optional.of(new VirtualIntentInstalling(networkId, processor,
+ IntentData.compiled(data, compiled), stored));
+ } catch (IntentException e) {
+ log.warn("Unable to compile intent {} due to:", data.intent(), e);
+ if (stored.filter(x -> !x.installables().isEmpty()).isPresent()) {
+ // removing orphaned flows and deallocating resources
+ return Optional.of(
+ new VirtualIntentWithdrawing(networkId, processor,
+ new IntentData(data, stored.get().installables())));
+ } else {
+ return Optional.of(new VirtualIntentFailed(data));
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java
new file mode 100644
index 0000000..dff1d13
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java
@@ -0,0 +1,44 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.FAILED;
+
+/**
+ * Represents a phase where the compile has failed.
+ */
+public class VirtualIntentFailed extends VirtualFinalIntentProcessPhase {
+
+ private final IntentData data;
+
+ /**
+ * Create an instance with the specified data.
+ *
+ * @param data intentData
+ */
+ VirtualIntentFailed(IntentData data) {
+ this.data = IntentData.nextState(checkNotNull(data), FAILED);
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java
new file mode 100644
index 0000000..08bfec7
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase where intent installation has been requested
+ * for a virtual network.
+ */
+final class VirtualIntentInstallRequest implements VirtualIntentProcessPhase {
+
+ private final NetworkId networkId;
+ private final VirtualIntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates an install request phase.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor to be passed to intent process phases
+ * generated after this phase
+ * @param intentData intent data to be processed
+ * @param stored intent data stored in the store
+ */
+ VirtualIntentInstallRequest(NetworkId networkId, VirtualIntentProcessor processor,
+ IntentData intentData, Optional<IntentData> stored) {
+ this.networkId = checkNotNull(networkId);
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<VirtualIntentProcessPhase> execute() {
+ transferErrorCount(data, stored);
+
+ return Optional.of(new VirtualIntentCompiling(networkId, processor, data, stored));
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java
new file mode 100644
index 0000000..cd8a185
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java
@@ -0,0 +1,66 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.INSTALLING;
+
+/**
+ * Represents a phase where an intent is being installed for a virtual network.
+ */
+//FIXME: better way to implement intent phase and processing for virtual networks?
+public class VirtualIntentInstalling extends VirtualFinalIntentProcessPhase {
+
+ private final NetworkId networkId;
+ private final VirtualIntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Create an installing phase.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor that does work for installing
+ * @param data intent data containing an intent to be installed
+ * @param stored intent data already stored
+ */
+ VirtualIntentInstalling(NetworkId networkId, VirtualIntentProcessor processor,
+ IntentData data,
+ Optional<IntentData> stored) {
+ this.networkId = checkNotNull(networkId);
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.stored = checkNotNull(stored);
+ this.data.setState(INSTALLING);
+ }
+
+ @Override
+ public void preExecute() {
+ processor.apply(networkId, stored, Optional.of(data));
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java
new file mode 100644
index 0000000..99fab54
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Represents a phase of processing an intent.
+ */
+public interface VirtualIntentProcessPhase {
+ /**
+ * Execute the procedure represented by the instance
+ * and generates the next update instance.
+ *
+ * @return next update
+ */
+ Optional<VirtualIntentProcessPhase> execute();
+
+ /**
+ * Create a starting intent process phase according to intent data this class holds.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor to be passed to intent process phases
+ * generated while this instance is working
+ * @param data intent data to be processed
+ * @param current intent date that is stored in the store
+ * @return starting intent process phase
+ */
+ static VirtualIntentProcessPhase newInitialPhase(NetworkId networkId,
+ VirtualIntentProcessor processor,
+ IntentData data, IntentData current) {
+ switch (data.request()) {
+ case INSTALL_REQ:
+ return new VirtualIntentInstallRequest(networkId, processor, data,
+ Optional.ofNullable(current));
+ case WITHDRAW_REQ:
+ return new VirtualIntentWithdrawRequest(networkId, processor, data,
+ Optional.ofNullable(current));
+ case PURGE_REQ:
+ return new VirtualIntentPurgeRequest(data, Optional.ofNullable(current));
+ default:
+ // illegal state
+ return new VirtualIntentFailed(data);
+ }
+ }
+
+ static VirtualFinalIntentProcessPhase process(VirtualIntentProcessPhase initial) {
+ Optional<VirtualIntentProcessPhase> currentPhase = Optional.of(initial);
+ VirtualIntentProcessPhase previousPhase = initial;
+
+ while (currentPhase.isPresent()) {
+ previousPhase = currentPhase.get();
+ currentPhase = previousPhase.execute();
+ }
+ return (VirtualFinalIntentProcessPhase) previousPhase;
+ }
+
+ static void transferErrorCount(IntentData data, Optional<IntentData> stored) {
+ stored.ifPresent(storedData -> {
+ if (Objects.equals(data.intent(), storedData.intent()) &&
+ Objects.equals(data.request(), storedData.request())) {
+ data.setErrorCount(storedData.errorCount());
+ } else {
+ data.setErrorCount(0);
+ }
+ });
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java
new file mode 100644
index 0000000..a6f29ea
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentState;
+import org.slf4j.Logger;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Represents a phase of requesting a purge of an intent for a virtual network.
+ * Note: The purge will only succeed if the intent is FAILED or WITHDRAWN.
+ */
+final class VirtualIntentPurgeRequest extends VirtualFinalIntentProcessPhase {
+ private static final Logger log = getLogger(VirtualIntentPurgeRequest.class);
+
+ private final IntentData data;
+ protected final Optional<IntentData> stored;
+
+ VirtualIntentPurgeRequest(IntentData intentData, Optional<IntentData> stored) {
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ private boolean shouldAcceptPurge() {
+ if (!stored.isPresent()) {
+ log.info("Purge for intent {}, but intent is not present",
+ data.key());
+ return true;
+ }
+
+ IntentData storedData = stored.get();
+ if (storedData.state() == IntentState.WITHDRAWN
+ || storedData.state() == IntentState.FAILED) {
+ return true;
+ }
+ log.info("Purge for intent {} is rejected because intent state is {}",
+ data.key(), storedData.state());
+ return false;
+ }
+
+ @Override
+ public IntentData data() {
+ if (shouldAcceptPurge()) {
+ return data;
+ } else {
+ return stored.get();
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java
new file mode 100644
index 0000000..ce8dfc9
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase of requesting a withdraw of an intent for a virtual network.
+ */
+final class VirtualIntentWithdrawRequest implements VirtualIntentProcessPhase {
+ private static final Logger log = LoggerFactory.getLogger(VirtualIntentWithdrawRequest.class);
+
+ private final NetworkId networkId;
+ private final VirtualIntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates a withdraw request phase.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor to be passed to intent process phases
+ * generated after this phase
+ * @param intentData intent data to be processed
+ * @param stored intent data stored in the store
+ */
+ VirtualIntentWithdrawRequest(NetworkId networkId, VirtualIntentProcessor processor,
+ IntentData intentData, Optional<IntentData> stored) {
+ this.networkId = checkNotNull(networkId);
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<VirtualIntentProcessPhase> execute() {
+ //TODO perhaps we want to validate that the pending and current are the
+ // same version i.e. they are the same
+ // Note: this call is not just the symmetric version of submit
+
+ transferErrorCount(data, stored);
+
+ if (!stored.isPresent() || stored.get().installables().isEmpty()) {
+ switch (data.request()) {
+ case INSTALL_REQ:
+ // illegal state?
+ log.warn("{} was requested to withdraw during installation?", data.intent());
+ return Optional.of(new VirtualIntentFailed(data));
+ case WITHDRAW_REQ:
+ default: //TODO "default" case should not happen
+ return Optional.of(new VirtualIntentWithdrawn(data));
+ }
+ }
+
+ return Optional.of(new VirtualIntentWithdrawing(networkId, processor,
+ new IntentData(data, stored.get().installables())));
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java
new file mode 100644
index 0000000..f7a2867
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java
@@ -0,0 +1,61 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWING;
+
+/**
+ * Represents a phase where an intent is withdrawing.
+ */
+final class VirtualIntentWithdrawing extends VirtualFinalIntentProcessPhase {
+
+ private final NetworkId networkId;
+ private final VirtualIntentProcessor processor;
+ private final IntentData data;
+
+ /**
+ * Creates a withdrawing phase.
+ *
+ * @param networkId virtual network identifier
+ * @param processor intent processor that does work for withdrawing
+ * @param data intent data containing an intent to be withdrawn
+ */
+ VirtualIntentWithdrawing(NetworkId networkId, VirtualIntentProcessor processor,
+ IntentData data) {
+ this.networkId = checkNotNull(networkId);
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.data.setState(WITHDRAWING);
+ }
+
+ @Override
+ protected void preExecute() {
+ processor.apply(networkId, Optional.of(data), Optional.empty());
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java
new file mode 100644
index 0000000..7e592c2
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java
@@ -0,0 +1,44 @@
+/*
+ * 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.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+
+/**
+ * Represents a phase where an intent has been withdrawn for a virtual network.
+ */
+final class VirtualIntentWithdrawn extends VirtualFinalIntentProcessPhase {
+
+ private final IntentData data;
+
+ /**
+ * Create a withdrawn phase.
+ *
+ * @param data intent data containing an intent to be withdrawn
+ */
+ VirtualIntentWithdrawn(IntentData data) {
+ this.data = IntentData.nextState(checkNotNull(data), WITHDRAWN);
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java
new file mode 100644
index 0000000..5fac168
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of various intent processing phases for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.impl.intent.phase;
\ No newline at end of file
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java
new file mode 100644
index 0000000..b0750eb
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/**
+ * Implementation of the virtual network subsystem.
+ */
+package org.onosproject.incubator.net.virtual.impl;
\ No newline at end of file
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java
new file mode 100644
index 0000000..436e1d4
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java
@@ -0,0 +1,792 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Table;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.BatchOperationEntry;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.copyOf;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider that translate virtual flow rules into physical rules.
+ * Current implementation is based on FlowRules.
+ * This virtualize and de-virtualize virtual flow rules into physical flow rules.
+ * {@link org.onosproject.net.flow.FlowRule}
+ */
+@Component(service = VirtualFlowRuleProvider.class)
+public class DefaultVirtualFlowRuleProvider extends AbstractVirtualProvider
+ implements VirtualFlowRuleProvider {
+
+ private static final String APP_ID_STR = "org.onosproject.virtual.vnet-flow_";
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualNetworkService vnService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualProviderRegistryService providerRegistryService;
+
+ private InternalRoutingAlgorithm internalRoutingAlgorithm;
+ private InternalVirtualFlowRuleManager frm;
+ private ApplicationId appId;
+ private FlowRuleListener flowRuleListener;
+
+ /**
+ * Creates a provider with the identifier.
+ */
+ public DefaultVirtualFlowRuleProvider() {
+ super(new ProviderId("vnet-flow", "org.onosproject.virtual.vnet-flow"));
+ }
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(APP_ID_STR);
+
+ providerRegistryService.registerProvider(this);
+
+ flowRuleListener = new InternalFlowRuleListener();
+ flowRuleService.addListener(flowRuleListener);
+
+ internalRoutingAlgorithm = new DefaultInternalRoutingAlgorithm();
+ frm = new InternalVirtualFlowRuleManager();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ flowRuleService.removeListener(flowRuleListener);
+ flowRuleService.removeFlowRulesById(appId);
+ providerRegistryService.unregisterProvider(this);
+ log.info("Stopped");
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ }
+
+ @Override
+ public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
+ for (FlowRule flowRule : flowRules) {
+ devirtualize(networkId, flowRule).forEach(
+ r -> flowRuleService.applyFlowRules(r));
+ }
+ }
+
+ @Override
+ public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
+ for (FlowRule flowRule : flowRules) {
+ devirtualize(networkId, flowRule).forEach(
+ r -> flowRuleService.removeFlowRules(r));
+ }
+ }
+
+ @Override
+ public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
+ checkNotNull(batch);
+
+ for (FlowRuleBatchEntry fop : batch.getOperations()) {
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+
+ switch (fop.operator()) {
+ case ADD:
+ devirtualize(networkId, fop.target()).forEach(builder::add);
+ break;
+ case REMOVE:
+ devirtualize(networkId, fop.target()).forEach(builder::remove);
+ break;
+ case MODIFY:
+ devirtualize(networkId, fop.target()).forEach(builder::modify);
+ break;
+ default:
+ break;
+ }
+
+ flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ CompletedBatchOperation status =
+ new CompletedBatchOperation(true,
+ Sets.newConcurrentHashSet(),
+ batch.deviceId());
+
+ VirtualFlowRuleProviderService providerService =
+ (VirtualFlowRuleProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualFlowRuleProvider.class);
+ providerService.batchOperationCompleted(batch.id(), status);
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ Set<FlowRule> failures = ImmutableSet.copyOf(
+ Lists.transform(batch.getOperations(),
+ BatchOperationEntry::target));
+
+ CompletedBatchOperation status =
+ new CompletedBatchOperation(false,
+ failures,
+ batch.deviceId());
+
+ VirtualFlowRuleProviderService providerService =
+ (VirtualFlowRuleProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualFlowRuleProvider.class);
+ providerService.batchOperationCompleted(batch.id(), status);
+ }
+ }));
+ }
+ }
+
+ public void setEmbeddingAlgorithm(InternalRoutingAlgorithm
+ internalRoutingAlgorithm) {
+ this.internalRoutingAlgorithm = internalRoutingAlgorithm;
+ }
+
+ /**
+ * Translate the requested physical flow rules into virtual flow rules.
+ *
+ * @param flowRule A virtual flow rule to be translated
+ * @return A flow rule for a specific virtual network
+ */
+ private FlowRule virtualizeFlowRule(FlowRule flowRule) {
+
+ FlowRule storedrule = frm.getVirtualRule(flowRule);
+
+ if (flowRule.reason() == FlowRule.FlowRemoveReason.NO_REASON) {
+ return storedrule;
+ } else {
+ return DefaultFlowRule.builder()
+ .withReason(flowRule.reason())
+ .withPriority(storedrule.priority())
+ .forDevice(storedrule.deviceId())
+ .forTable(storedrule.tableId())
+ .fromApp(new DefaultApplicationId(storedrule.appId(), null))
+ .withIdleTimeout(storedrule.timeout())
+ .withHardTimeout(storedrule.hardTimeout())
+ .withSelector(storedrule.selector())
+ .withTreatment(storedrule.treatment())
+ .build();
+ }
+ }
+
+ private FlowEntry virtualize(FlowEntry flowEntry) {
+ FlowRule vRule = virtualizeFlowRule(flowEntry);
+
+ if (vRule == null) {
+ return null;
+ }
+
+ FlowEntry vEntry = new DefaultFlowEntry(vRule, flowEntry.state(),
+ flowEntry.life(),
+ flowEntry.packets(),
+ flowEntry.bytes());
+ return vEntry;
+ }
+
+ /**
+ * Translate the requested virtual flow rules into physical flow rules.
+ * The translation could be one to many.
+ *
+ * @param flowRule A flow rule from underlying data plane to be translated
+ * @return A set of flow rules for physical network
+ */
+ private Set<FlowRule> devirtualize(NetworkId networkId, FlowRule flowRule) {
+
+ Set<FlowRule> outRules = new HashSet<>();
+
+ Set<ConnectPoint> ingressPoints = extractIngressPoints(networkId,
+ flowRule.deviceId(),
+ flowRule.selector());
+
+ ConnectPoint egressPoint = extractEgressPoints(networkId,
+ flowRule.deviceId(),
+ flowRule.treatment());
+
+ if (egressPoint == null) {
+ return outRules;
+ }
+
+ TrafficSelector.Builder commonSelectorBuilder
+ = DefaultTrafficSelector.builder();
+ flowRule.selector().criteria().stream()
+ .filter(c -> c.type() != Criterion.Type.IN_PORT)
+ .forEach(c -> commonSelectorBuilder.add(c));
+ TrafficSelector commonSelector = commonSelectorBuilder.build();
+
+ TrafficTreatment.Builder commonTreatmentBuilder
+ = DefaultTrafficTreatment.builder();
+ flowRule.treatment().allInstructions().stream()
+ .filter(i -> i.type() != Instruction.Type.OUTPUT)
+ .forEach(i -> commonTreatmentBuilder.add(i));
+ TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
+
+ for (ConnectPoint ingressPoint : ingressPoints) {
+ if (egressPoint.port() == PortNumber.FLOOD) {
+ Set<ConnectPoint> outPoints = vnService
+ .getVirtualPorts(networkId, flowRule.deviceId())
+ .stream()
+ .map(VirtualPort::realizedBy)
+ .filter(p -> !p.equals(ingressPoint))
+ .collect(Collectors.toSet());
+
+ for (ConnectPoint outPoint : outPoints) {
+ outRules.addAll(generateRules(networkId, ingressPoint, outPoint,
+ commonSelector, commonTreatment, flowRule));
+ }
+ } else {
+ outRules.addAll(generateRules(networkId, ingressPoint, egressPoint,
+ commonSelector, commonTreatment, flowRule));
+ }
+ }
+
+ return outRules;
+ }
+
+ /**
+ * Extract ingress connect points of the physical network
+ * from the requested traffic selector.
+ *
+ * @param networkId the virtual network identifier
+ * @param deviceId the virtual device identifier
+ * @param selector the traffic selector to extract ingress point
+ * @return the set of ingress connect points of the physical network
+ */
+ private Set<ConnectPoint> extractIngressPoints(NetworkId networkId,
+ DeviceId deviceId,
+ TrafficSelector selector) {
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+
+ Set<VirtualPort> vPorts = vnService
+ .getVirtualPorts(networkId, deviceId);
+
+ PortCriterion portCriterion = ((PortCriterion) selector
+ .getCriterion(Criterion.Type.IN_PORT));
+
+ if (portCriterion != null) {
+ PortNumber vInPortNum = portCriterion.port();
+
+ Optional<ConnectPoint> optionalCp = vPorts.stream()
+ .filter(v -> v.number().equals(vInPortNum))
+ .map(VirtualPort::realizedBy).findFirst();
+ if (!optionalCp.isPresent()) {
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ vInPortNum, networkId, deviceId);
+ return ingressPoints;
+ }
+
+ ingressPoints.add(optionalCp.get());
+ } else {
+ for (VirtualPort vPort : vPorts) {
+ if (vPort.realizedBy() != null) {
+ ingressPoints.add(vPort.realizedBy());
+ } else {
+ log.warn("Port {} is not realized yet, in Network {}, " +
+ "Device {}",
+ vPort, networkId, deviceId);
+ }
+ }
+ }
+
+ return ingressPoints;
+ }
+
+ /**
+ * Extract egress connect point of the physical network
+ * from the requested traffic treatment.
+ *
+ * @param networkId the virtual network identifier
+ * @param deviceId the virtual device identifier
+ * @param treatment the traffic treatment to extract ingress point
+ * @return the egress connect point of the physical network
+ */
+ private ConnectPoint extractEgressPoints(NetworkId networkId,
+ DeviceId deviceId,
+ TrafficTreatment treatment) {
+
+ Set<VirtualPort> vPorts = vnService
+ .getVirtualPorts(networkId, deviceId);
+
+ PortNumber vOutPortNum = treatment.allInstructions().stream()
+ .filter(i -> i.type() == Instruction.Type.OUTPUT)
+ .map(i -> ((Instructions.OutputInstruction) i).port())
+ .findFirst().get();
+
+ Optional<ConnectPoint> optionalCpOut = vPorts.stream()
+ .filter(v -> v.number().equals(vOutPortNum))
+ .map(VirtualPort::realizedBy)
+ .findFirst();
+
+ if (!optionalCpOut.isPresent()) {
+ if (vOutPortNum.isLogical()) {
+ return new ConnectPoint(DeviceId.deviceId("vNet"), vOutPortNum);
+ }
+
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ vOutPortNum, networkId, deviceId);
+ return null;
+ }
+
+ return optionalCpOut.get();
+ }
+
+
+ /**
+ * Generates the corresponding flow rules for the physical network.
+ *
+ * @param networkId The virtual network identifier
+ * @param ingressPoint The ingress point of the physical network
+ * @param egressPoint The egress point of the physical network
+ * @param commonSelector A common traffic selector between the virtual
+ * and physical flow rules
+ * @param commonTreatment A common traffic treatment between the virtual
+ * and physical flow rules
+ * @param flowRule The virtual flow rule to be translated
+ * @return A set of flow rules for the physical network
+ */
+ private Set<FlowRule> generateRules(NetworkId networkId,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint,
+ TrafficSelector commonSelector,
+ TrafficTreatment commonTreatment,
+ FlowRule flowRule) {
+
+ if (ingressPoint.deviceId().equals(egressPoint.deviceId()) ||
+ egressPoint.port().isLogical()) {
+ return generateRuleForSingle(networkId, ingressPoint, egressPoint,
+ commonSelector, commonTreatment, flowRule);
+ } else {
+ return generateRuleForMulti(networkId, ingressPoint, egressPoint,
+ commonSelector, commonTreatment, flowRule);
+ }
+ }
+
+ /**
+ * Generate physical rules when a virtual flow rule can be handled inside
+ * a single physical switch.
+ *
+ * @param networkId The virtual network identifier
+ * @param ingressPoint The ingress point of the physical network
+ * @param egressPoint The egress point of the physical network
+ * @param commonSelector A common traffic selector between the virtual
+ * and physical flow rules
+ * @param commonTreatment A common traffic treatment between the virtual
+ * and physical flow rules
+ * @param flowRule The virtual flow rule to be translated
+ * @return A set of flow rules for the physical network
+ */
+ private Set<FlowRule> generateRuleForSingle(NetworkId networkId,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint,
+ TrafficSelector commonSelector,
+ TrafficTreatment commonTreatment,
+ FlowRule flowRule) {
+
+ Set<FlowRule> outRules = new HashSet<>();
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
+ .builder(commonSelector)
+ .matchInPort(ingressPoint.port());
+
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(egressPoint.port());
+
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
+ .forDevice(ingressPoint.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withIdleTimeout(flowRule.timeout())
+ .withPriority(flowRule.priority());
+
+ FlowRule rule = ruleBuilder.build();
+ frm.addIngressRule(flowRule, rule, networkId);
+ outRules.add(rule);
+
+ return outRules;
+ }
+
+ /**
+ * Generate physical rules when a virtual flow rule can be handled with
+ * multiple physical switches.
+ *
+ * @param networkId The virtual network identifier
+ * @param ingressPoint The ingress point of the physical network
+ * @param egressPoint The egress point of the physical network
+ * @param commonSelector A common traffic selector between the virtual
+ * and physical flow rules
+ * @param commonTreatment A common traffic treatment between the virtual
+ * and physical flow rules
+ * @param flowRule The virtual flow rule to be translated
+ * @return A set of flow rules for the physical network
+ */
+ private Set<FlowRule> generateRuleForMulti(NetworkId networkId,
+ ConnectPoint ingressPoint,
+ ConnectPoint egressPoint,
+ TrafficSelector commonSelector,
+ TrafficTreatment commonTreatment,
+ FlowRule flowRule) {
+ Set<FlowRule> outRules = new HashSet<>();
+
+ Path internalPath = internalRoutingAlgorithm
+ .findPath(ingressPoint, egressPoint);
+ checkNotNull(internalPath, "No path between " +
+ ingressPoint.toString() + " " + egressPoint.toString());
+
+ ConnectPoint outCp = internalPath.links().get(0).src();
+
+ //ingress point of tunnel
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder(commonSelector);
+ selectorBuilder.matchInPort(ingressPoint.port());
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder(commonTreatment);
+ //TODO: add the logic to check host location
+ treatmentBuilder.pushVlan()
+ .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
+ treatmentBuilder.setOutput(outCp.port());
+
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
+ .forDevice(ingressPoint.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withIdleTimeout(flowRule.timeout())
+ .withTreatment(treatmentBuilder.build())
+ .withPriority(flowRule.priority());
+
+ FlowRule rule = ruleBuilder.build();
+ frm.addIngressRule(flowRule, rule, networkId);
+ outRules.add(rule);
+
+ //routing inside tunnel
+ ConnectPoint inCp = internalPath.links().get(0).dst();
+
+ if (internalPath.links().size() > 1) {
+ for (Link l : internalPath.links()
+ .subList(1, internalPath.links().size())) {
+
+ outCp = l.src();
+
+ selectorBuilder = DefaultTrafficSelector
+ .builder(commonSelector)
+ .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
+ .matchInPort(inCp.port());
+
+ treatmentBuilder = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(outCp.port());
+
+ ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
+ .forDevice(inCp.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withIdleTimeout(flowRule.timeout())
+ .withPriority(flowRule.priority());
+
+ outRules.add(ruleBuilder.build());
+ inCp = l.dst();
+ }
+ }
+
+ //egress point of tunnel
+ selectorBuilder = DefaultTrafficSelector.builder(commonSelector)
+ .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
+ .matchInPort(inCp.port());
+
+ treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment)
+ .popVlan()
+ .setOutput(egressPoint.port());
+
+ ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .forDevice(egressPoint.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withIdleTimeout(flowRule.timeout())
+ .withPriority(flowRule.priority());
+
+ outRules.add(ruleBuilder.build());
+
+ return outRules;
+ }
+
+ private class InternalFlowRuleListener implements FlowRuleListener {
+ @Override
+ public void event(FlowRuleEvent event) {
+ if ((event.type() == FlowRuleEvent.Type.RULE_ADDED) ||
+ (event.type() == FlowRuleEvent.Type.RULE_UPDATED)) {
+ if (frm.isVirtualIngressRule(event.subject())) {
+ NetworkId networkId = frm.getVirtualNetworkId(event.subject());
+ FlowEntry vEntry = getVirtualFlowEntry(event.subject());
+
+ if (vEntry == null) {
+ return;
+ }
+
+ frm.addOrUpdateFlowEntry(networkId, vEntry.deviceId(), vEntry);
+
+ VirtualFlowRuleProviderService providerService =
+ (VirtualFlowRuleProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualFlowRuleProvider.class);
+
+ ImmutableList.Builder<FlowEntry> builder = ImmutableList.builder();
+ builder.addAll(frm.getFlowEntries(networkId, vEntry.deviceId()));
+
+ providerService.pushFlowMetrics(vEntry.deviceId(), builder.build());
+ }
+ } else if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
+ if (frm.isVirtualIngressRule(event.subject())) {
+ //FIXME confirm all physical rules are removed
+ NetworkId networkId = frm.getVirtualNetworkId(event.subject());
+ FlowEntry vEntry = getVirtualFlowEntry(event.subject());
+
+ if (vEntry == null) {
+ return;
+ }
+
+ frm.removeFlowEntry(networkId, vEntry.deviceId(), vEntry);
+ frm.removeFlowRule(networkId, vEntry.deviceId(), vEntry);
+
+ VirtualFlowRuleProviderService providerService =
+ (VirtualFlowRuleProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualFlowRuleProvider.class);
+ providerService.flowRemoved(vEntry);
+ }
+ }
+ }
+
+ private FlowEntry getVirtualFlowEntry(FlowRule rule) {
+ FlowEntry entry = null;
+ for (FlowEntry fe :
+ flowRuleService.getFlowEntries(rule.deviceId())) {
+ if (rule.exactMatch(fe)) {
+ entry = fe;
+ }
+ }
+
+ if (entry != null) {
+ return virtualize(entry);
+ } else {
+ return virtualize(new DefaultFlowEntry(rule,
+ FlowEntry.FlowEntryState.PENDING_REMOVE));
+ }
+ }
+ }
+
+ private class InternalVirtualFlowRuleManager {
+ /** <Virtual Network ID, Virtual Device ID, Virtual Flow Rules>.*/
+ final Table<NetworkId, DeviceId, Set<FlowRule>> flowRuleTable
+ = HashBasedTable.create();
+
+ /** <Virtual Network ID, Virtual Device ID, Virtual Flow Entries>.*/
+ final Table<NetworkId, DeviceId, Set<FlowEntry>> flowEntryTable
+ = HashBasedTable.create();
+
+ /** <Physical Flow Rule, Virtual Network ID>.*/
+ final Map<FlowRule, NetworkId> ingressRuleMap = Maps.newHashMap();
+
+ /** <Physical Flow Rule, Virtual Virtual Flow Rule>.*/
+ final Map<FlowRule, FlowRule> virtualizationMap = Maps.newHashMap();
+
+ private Iterable<FlowRule> getFlowRules(NetworkId networkId,
+ DeviceId deviceId) {
+ return flowRuleTable.get(networkId, deviceId);
+ }
+
+ private Iterable<FlowEntry> getFlowEntries(NetworkId networkId,
+ DeviceId deviceId) {
+ return flowEntryTable.get(networkId, deviceId);
+ }
+
+ private void addFlowRule(NetworkId networkId, DeviceId deviceId,
+ FlowRule flowRule) {
+ Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
+ if (set == null) {
+ set = Sets.newHashSet();
+ flowRuleTable.put(networkId, deviceId, set);
+ }
+ set.add(flowRule);
+ }
+
+ private void removeFlowRule(NetworkId networkId, DeviceId deviceId,
+ FlowRule flowRule) {
+ Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
+ if (set == null) {
+ return;
+ }
+ set.remove(flowRule);
+ }
+
+ private void addOrUpdateFlowEntry(NetworkId networkId, DeviceId deviceId,
+ FlowEntry flowEntry) {
+ Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
+ if (set == null) {
+ set = Sets.newConcurrentHashSet();
+ flowEntryTable.put(networkId, deviceId, set);
+ }
+
+ //Replace old entry with new one
+ set.stream().filter(fe -> fe.exactMatch(flowEntry))
+ .forEach(set::remove);
+ set.add(flowEntry);
+ }
+
+ private void removeFlowEntry(NetworkId networkId, DeviceId deviceId,
+ FlowEntry flowEntry) {
+ Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
+ if (set == null) {
+ return;
+ }
+ set.remove(flowEntry);
+ }
+
+ private void addIngressRule(FlowRule virtualRule, FlowRule physicalRule,
+ NetworkId networkId) {
+ ingressRuleMap.put(physicalRule, networkId);
+ virtualizationMap.put(physicalRule, virtualRule);
+ }
+
+ private FlowRule getVirtualRule(FlowRule physicalRule) {
+ return virtualizationMap.get(physicalRule);
+ }
+
+ private void removeIngressRule(FlowRule physicalRule) {
+ ingressRuleMap.remove(physicalRule);
+ virtualizationMap.remove(physicalRule);
+ }
+
+ private Set<FlowRule> getAllPhysicalRule() {
+ return copyOf(virtualizationMap.keySet());
+ }
+
+ private NetworkId getVirtualNetworkId(FlowRule physicalRule) {
+ return ingressRuleMap.get(physicalRule);
+ }
+
+ /**
+ * Test the rule is the ingress rule for virtual rules.
+ *
+ * @param flowRule A flow rule from underlying data plane to be translated
+ * @return True when the rule is for ingress point for a virtual switch
+ */
+ private boolean isVirtualIngressRule(FlowRule flowRule) {
+ return ingressRuleMap.containsKey(flowRule);
+ }
+ }
+
+ private class DefaultInternalRoutingAlgorithm
+ implements InternalRoutingAlgorithm {
+
+ @Override
+ public Path findPath(ConnectPoint src, ConnectPoint dst) {
+ Set<Path> paths =
+ topologyService.getPaths(topologyService.currentTopology(),
+ src.deviceId(),
+ dst.deviceId());
+
+ if (paths.isEmpty()) {
+ return null;
+ }
+
+ //TODO the logic find the best path
+ return (Path) paths.toArray()[0];
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualGroupProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualGroupProvider.java
new file mode 100644
index 0000000..7cc86cc
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualGroupProvider.java
@@ -0,0 +1,128 @@
+/*
+ * 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.impl.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualGroupProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider to handle Group for virtual network.
+ */
+@Component(service = VirtualGroupProvider.class)
+public class DefaultVirtualGroupProvider extends AbstractVirtualProvider
+ implements VirtualGroupProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualProviderRegistryService providerRegistryService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected GroupService groupService;
+
+ private InternalGroupEventListener internalGroupEventListener;
+
+ /**
+ * Creates a virtual provider with the supplied identifier.
+ */
+ public DefaultVirtualGroupProvider() {
+ super(new ProviderId("vnet-group", "org.onosproject.virtual.of-group"));
+ }
+
+ @Activate
+ public void activate() {
+ providerRegistryService.registerProvider(this);
+
+ internalGroupEventListener = new InternalGroupEventListener();
+ groupService.addListener(internalGroupEventListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ groupService.removeListener(internalGroupEventListener);
+ providerRegistryService.unregisterProvider(this);
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ }
+
+ @Override
+ public void performGroupOperation(NetworkId networkId, DeviceId deviceId, GroupOperations groupOps) {
+ for (GroupOperation groupOperation: groupOps.operations()) {
+ switch (groupOperation.opType()) {
+ case ADD:
+ //TODO: devirtualize + groupAdd
+ log.info("Group Add is not supported, yet");
+ break;
+ case MODIFY:
+ //TODO: devirtualize + groupMod
+ log.info("Group Modify is not supported, yet");
+ break;
+ case DELETE:
+ //TODO: devirtualize + groupDel
+ log.info("Group Delete is not supported, yet");
+ break;
+ default:
+ log.error("Unsupported Group operation");
+ return;
+ }
+ }
+ }
+
+ private class InternalGroupEventListener implements GroupListener {
+ @Override
+ public void event(GroupEvent event) {
+ switch (event.type()) {
+ //TODO: virtualize + notify to virtual provider service
+ case GROUP_ADD_REQUESTED:
+ case GROUP_UPDATE_REQUESTED:
+ case GROUP_REMOVE_REQUESTED:
+ case GROUP_ADDED:
+ case GROUP_UPDATED:
+ case GROUP_REMOVED:
+ case GROUP_ADD_FAILED:
+ case GROUP_UPDATE_FAILED:
+ case GROUP_REMOVE_FAILED:
+ case GROUP_BUCKET_FAILOVER:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java
new file mode 100644
index 0000000..26462ad
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualMeterProvider.java
@@ -0,0 +1,264 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalCause;
+import com.google.common.cache.RemovalNotification;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterEvent;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterListener;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider to handle meters for virtual networks.
+ */
+@Component(service = VirtualMeterProvider.class)
+public class DefaultVirtualMeterProvider extends AbstractVirtualProvider
+ implements VirtualMeterProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ static final long TIMEOUT = 30;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualProviderRegistryService providerRegistryService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MeterService meterService;
+
+ private MeterListener internalMeterListener;
+ private Cache<Long, VirtualMeterOperation> pendingOperations;
+ private IdGenerator idGenerator;
+
+ @Activate
+ public void activate() {
+ providerRegistryService.registerProvider(this);
+ internalMeterListener = new InternalMeterListener();
+
+ idGenerator = getIdGenerator();
+
+ pendingOperations = CacheBuilder.newBuilder()
+ .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
+ .removalListener(
+ (RemovalNotification<Long, VirtualMeterOperation>
+ notification) -> {
+ if (notification.getCause() == RemovalCause.EXPIRED) {
+ NetworkId networkId = notification.getValue().networkId();
+ MeterOperation op = notification.getValue().operation();
+
+ VirtualMeterProviderService providerService =
+ (VirtualMeterProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualMeterProvider.class);
+
+ providerService.meterOperationFailed(op,
+ MeterFailReason.TIMEOUT);
+ }
+ }).build();
+
+ meterService.addListener(internalMeterListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ meterService.removeListener(internalMeterListener);
+ providerRegistryService.unregisterProvider(this);
+ }
+
+ /**
+ * Creates a provider with the identifier.
+ */
+ public DefaultVirtualMeterProvider() {
+ super(new ProviderId("vnet-meter",
+ "org.onosproject.virtual.vnet-meter"));
+ }
+
+ @Override
+ public void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperations meterOps) {
+ meterOps.operations().forEach(op -> performOperation(networkId, deviceId, op));
+ }
+
+ @Override
+ public void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperation meterOp) {
+ performOperation(networkId, deviceId, meterOp);
+ }
+
+ private void performOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperation op) {
+
+ VirtualMeterOperation vOp = new VirtualMeterOperation(networkId, op);
+ pendingOperations.put(idGenerator.getNewId(), vOp);
+
+ switch (op.type()) {
+ case ADD:
+ //TODO: devirtualize + submit
+ break;
+ case REMOVE:
+ //TODO: devirtualize + withdraw
+ break;
+ case MODIFY:
+ //TODO: devitualize + withdraw and submit
+ break;
+ default:
+ log.warn("Unknown Meter command {}; not sending anything",
+ op.type());
+ VirtualMeterProviderService providerService =
+ (VirtualMeterProviderService) providerRegistryService
+ .getProviderService(networkId,
+ VirtualMeterProvider.class);
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_COMMAND);
+ }
+
+ }
+
+ /**
+ * De-virtualizes a meter operation.
+ * It takes a virtual meter operation, and translate it to a physical meter operation.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId a virtual network device identifier
+ * @param meterOps a meter operation to be de-virtualized
+ * @return de-virtualized meter operation
+ */
+ private VirtualMeterOperation devirtualize(NetworkId networkId,
+ DeviceId deviceId,
+ MeterOperation meterOps) {
+ return null;
+ }
+
+ /**
+ * Virtualizes meter.
+ * This translates meter events for virtual networks before delivering them.
+ *
+ * @param meter
+ * @return
+ */
+ private Meter virtualize(Meter meter) {
+ return null;
+ }
+
+
+ private class InternalMeterListener implements MeterListener {
+ @Override
+ public void event(MeterEvent event) {
+ //TODO: virtualize + notify event to meter provider service
+ //Is it enough to enable virtual network provider?
+ switch (event.type()) {
+ case METER_ADD_REQ:
+ break;
+ case METER_REM_REQ:
+ break;
+ case METER_ADDED:
+ break;
+ case METER_REMOVED:
+ break;
+ default:
+ log.warn("Unknown meter event {}", event.type());
+ }
+ }
+ }
+
+ /**
+ * A class to hold a network identifier and a meter operation.
+ * This class is designed to be used only in virtual network meter provider.
+ */
+ private final class VirtualMeterOperation {
+ private NetworkId networkId;
+ private MeterOperation op;
+
+ private VirtualMeterOperation(NetworkId networkId, MeterOperation op) {
+ this.networkId = networkId;
+ this.op = op;
+ }
+
+ private NetworkId networkId() {
+ return networkId;
+ }
+
+ private MeterOperation operation() {
+ return this.op;
+ }
+ }
+
+ /**
+ * A class to hold a network identifier and a meter.
+ * This class is designed to be used in only virtual network meter provider.
+ */
+ private final class VirtualMeter {
+ private NetworkId networkId;
+ private Meter meter;
+
+ private VirtualMeter(NetworkId networkId, Meter meter) {
+ this.networkId = networkId;
+ this.meter = meter;
+ }
+
+ private NetworkId networkId() {
+ return this.networkId;
+ }
+
+ private Meter meter() {
+ return this.meter;
+ }
+ }
+
+ /**
+ * Id generator for virtual meters to guarantee the uniqueness of its identifier
+ * among multiple virtual network meters.
+ *
+ * @return an ID generator
+ */
+ private IdGenerator getIdGenerator() {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualNetworkProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualNetworkProvider.java
new file mode 100644
index 0000000..3860dee
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualNetworkProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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.impl.provider;
+
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyCluster;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Virtual network topology provider.
+ */
+@Component(service = VirtualNetworkProvider.class)
+public class DefaultVirtualNetworkProvider
+ extends AbstractProvider implements VirtualNetworkProvider {
+
+ private final Logger log = getLogger(DefaultVirtualNetworkProvider.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualNetworkProviderRegistry providerRegistry;
+
+ private VirtualNetworkProviderService providerService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected TopologyService topologyService;
+
+ protected TopologyListener topologyListener = new InternalTopologyListener();
+
+ private ExecutorService executor;
+
+ /**
+ * Default constructor.
+ */
+ public DefaultVirtualNetworkProvider() {
+ super(DefaultVirtualLink.PID);
+ }
+
+ @Activate
+ public void activate() {
+ executor = newSingleThreadExecutor(groupedThreads("onos/vnet", "provider", log));
+ providerService = providerRegistry.register(this);
+ topologyService.addListener(topologyListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ topologyService.removeListener(topologyListener);
+ executor.shutdownNow();
+ executor = null;
+ providerRegistry.unregister(this);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ @Override
+ public boolean isTraversable(ConnectPoint src, ConnectPoint dst) {
+ final boolean[] foundSrc = new boolean[1];
+ final boolean[] foundDst = new boolean[1];
+ Topology topology = topologyService.currentTopology();
+ Set<Path> paths = topologyService.getPaths(topology, src.deviceId(), dst.deviceId());
+ paths.forEach(path -> {
+ foundDst[0] = false;
+ foundSrc[0] = false;
+ // Traverse the links in each path to determine if both the src and dst connection
+ // point are in the path, if so then this src/dst pair are traversable.
+ path.links().forEach(link -> {
+ if (link.src().equals(src)) {
+ foundSrc[0] = true;
+ }
+ if (link.dst().equals(dst)) {
+ foundDst[0] = true;
+ }
+ });
+ if (foundSrc[0] && foundDst[0]) {
+ return;
+ }
+ });
+ return foundSrc[0] && foundDst[0];
+ }
+
+ /**
+ * Returns a set of set of interconnected connect points in the default topology.
+ * The inner set represents the interconnected connect points, and the outerset
+ * represents separate clusters.
+ *
+ * @param topology the default topology
+ * @return set of set of interconnected connect points.
+ */
+ public Set<Set<ConnectPoint>> getConnectPoints(Topology topology) {
+ Set<Set<ConnectPoint>> clusters = new HashSet<>();
+ Set<TopologyCluster> topologyClusters = topologyService.getClusters(topology);
+ topologyClusters.forEach(topologyCluster -> {
+ Set<ConnectPoint> connectPointSet = new HashSet<>();
+ Set<Link> clusterLinks =
+ topologyService.getClusterLinks(topology, topologyCluster);
+ clusterLinks.forEach(link -> {
+ connectPointSet.add(link.src());
+ connectPointSet.add(link.dst());
+ });
+ if (!connectPointSet.isEmpty()) {
+ clusters.add(connectPointSet);
+ }
+ });
+ return clusters;
+ }
+
+ /**
+ * Topology event listener.
+ */
+ private class InternalTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ // Perform processing off the listener thread.
+ executor.submit(() -> providerService
+ .topologyChanged(getConnectPoints(event.subject())));
+ }
+
+ @Override
+ public boolean isRelevant(TopologyEvent event) {
+ return event.type() == TopologyEvent.Type.TOPOLOGY_CHANGED &&
+ event.reasons().stream().anyMatch(reason -> reason instanceof LinkEvent);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.java
new file mode 100644
index 0000000..014f436
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketContext.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.impl.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualPacketContext;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+
+/**
+ *Default implementation of a virtual packet context.
+ */
+public class DefaultVirtualPacketContext extends DefaultPacketContext
+ implements VirtualPacketContext {
+
+ private NetworkId networkId;
+ private DefaultVirtualPacketProvider dvpp;
+
+ /**
+ * Creates a new packet context.
+ *
+ * @param time creation time
+ * @param inPkt inbound packet
+ * @param outPkt outbound packet
+ * @param block whether the context is blocked or not
+ * @param networkId virtual network ID where this context is handled
+ * @param dvpp pointer to default virtual packet provider
+ */
+
+ protected DefaultVirtualPacketContext(long time, InboundPacket inPkt,
+ OutboundPacket outPkt, boolean block,
+ NetworkId networkId,
+ DefaultVirtualPacketProvider dvpp) {
+ super(time, inPkt, outPkt, block);
+
+ this.networkId = networkId;
+ this.dvpp = dvpp;
+ }
+
+ @Override
+ public void send() {
+ if (!this.block()) {
+ dvpp.send(this);
+ }
+ }
+
+ @Override
+ public NetworkId networkId() {
+ return networkId;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java
new file mode 100644
index 0000000..9048954
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProvider.java
@@ -0,0 +1,421 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.collect.Sets;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
+import org.onosproject.incubator.net.virtual.VirtualPacketContext;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(service = VirtualPacketProvider.class)
+public class DefaultVirtualPacketProvider extends AbstractVirtualProvider
+ implements VirtualPacketProvider {
+
+ private static final int PACKET_PROCESSOR_PRIORITY = 1;
+ private static final PacketPriority VIRTUAL_PACKET_PRIORITY = PacketPriority.REACTIVE;
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualNetworkAdminService vnaService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualProviderRegistryService providerRegistryService;
+
+ private final VirtualNetworkListener virtualNetListener = new InternalVirtualNetworkListener();
+
+ private InternalPacketProcessor processor = null;
+
+ private Set<NetworkId> networkIdSet = Sets.newConcurrentHashSet();
+
+ private ApplicationId appId;
+
+ /**
+ * Creates a provider with the supplied identifier.
+ */
+ public DefaultVirtualPacketProvider() {
+ super(new ProviderId("virtual-packet", "org.onosproject.virtual.virtual-packet"));
+ }
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.virtual.virtual-packet");
+ providerRegistryService.registerProvider(this);
+ vnaService.addListener(virtualNetListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+
+ providerRegistryService.unregisterProvider(this);
+ vnaService.removeListener(virtualNetListener);
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ }
+
+
+ @Override
+ public void emit(NetworkId networkId, OutboundPacket packet) {
+ devirtualize(networkId, packet)
+ .forEach(outboundPacket -> packetService.emit(outboundPacket));
+ }
+
+ /**
+ * Just for test.
+ */
+ protected void startPacketHandling() {
+ processor = new InternalPacketProcessor();
+ packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
+ }
+
+ /**
+ * Send the outbound packet of a virtual context.
+ * This method is designed to support Context's send() method that invoked
+ * by applications.
+ * See {@link org.onosproject.net.packet.PacketContext}
+ *
+ * @param virtualPacketContext virtual packet context
+ */
+ protected void send(VirtualPacketContext virtualPacketContext) {
+ devirtualizeContext(virtualPacketContext)
+ .forEach(outboundPacket -> packetService.emit(outboundPacket));
+ }
+
+ /**
+ * Translate the requested physical PacketContext into a virtual PacketContext.
+ * See {@link org.onosproject.net.packet.PacketContext}
+ *
+ * @param context A physical PacketContext be translated
+ * @return A translated virtual PacketContext
+ */
+ private VirtualPacketContext virtualize(PacketContext context) {
+
+ VirtualPort vPort = getMappedVirtualPort(context.inPacket().receivedFrom());
+
+ if (vPort != null) {
+ ConnectPoint cp = new ConnectPoint(vPort.element().id(),
+ vPort.number());
+
+ Ethernet eth = context.inPacket().parsed();
+ eth.setVlanID(Ethernet.VLAN_UNTAGGED);
+
+ InboundPacket inPacket =
+ new DefaultInboundPacket(cp, eth,
+ ByteBuffer.wrap(eth.serialize()));
+
+ DefaultOutboundPacket outPkt =
+ new DefaultOutboundPacket(cp.deviceId(),
+ DefaultTrafficTreatment.builder().build(),
+ ByteBuffer.wrap(eth.serialize()));
+
+ VirtualPacketContext vContext =
+ new DefaultVirtualPacketContext(context.time(), inPacket, outPkt,
+ false, vPort.networkId(),
+ this);
+
+ return vContext;
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * Find the corresponding virtual port with the physical port.
+ *
+ * @param cp the connect point for the physical network
+ * @return a virtual port
+ */
+ private VirtualPort getMappedVirtualPort(ConnectPoint cp) {
+ Set<TenantId> tIds = vnaService.getTenantIds();
+
+ Set<VirtualNetwork> vNetworks = new HashSet<>();
+ tIds.forEach(tid -> vNetworks.addAll(vnaService.getVirtualNetworks(tid)));
+
+ for (VirtualNetwork vNet : vNetworks) {
+ Set<VirtualDevice> vDevices = vnaService.getVirtualDevices(vNet.id());
+
+ Set<VirtualPort> vPorts = new HashSet<>();
+ vDevices.forEach(dev -> vPorts
+ .addAll(vnaService.getVirtualPorts(dev.networkId(), dev.id())));
+
+ VirtualPort vPort = vPorts.stream()
+ .filter(vp -> vp.realizedBy().equals(cp))
+ .findFirst().orElse(null);
+
+ if (vPort != null) {
+ return vPort;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Translate the requested virtual outbound packet into
+ * a set of physical OutboundPacket.
+ * See {@link org.onosproject.net.packet.OutboundPacket}
+ *
+ * @param packet an OutboundPacket to be translated
+ * @return a set of de-virtualized (physical) OutboundPacket
+ */
+ private Set<OutboundPacket> devirtualize(NetworkId networkId, OutboundPacket packet) {
+ Set<OutboundPacket> outboundPackets = new HashSet<>();
+ Set<VirtualPort> vPorts = vnaService
+ .getVirtualPorts(networkId, packet.sendThrough());
+
+ TrafficTreatment.Builder commonTreatmentBuilder
+ = DefaultTrafficTreatment.builder();
+ packet.treatment().allInstructions().stream()
+ .filter(i -> i.type() != Instruction.Type.OUTPUT)
+ .forEach(i -> commonTreatmentBuilder.add(i));
+ TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
+
+ PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
+ .filter(i -> i.type() == Instruction.Type.OUTPUT)
+ .map(i -> ((Instructions.OutputInstruction) i).port())
+ .findFirst().get();
+
+ if (!vOutPortNum.isLogical()) {
+ Optional<ConnectPoint> optionalCpOut = vPorts.stream()
+ .filter(v -> v.number().equals(vOutPortNum))
+ .map(v -> v.realizedBy())
+ .findFirst();
+ if (!optionalCpOut.isPresent()) {
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ vOutPortNum, networkId, packet.sendThrough());
+ return outboundPackets;
+ }
+ ConnectPoint egressPoint = optionalCpOut.get();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(egressPoint.port()).build();
+
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(
+ egressPoint.deviceId(), treatment, packet.data());
+ outboundPackets.add(outboundPacket);
+ } else {
+ if (vOutPortNum == PortNumber.FLOOD) {
+ for (VirtualPort outPort : vPorts) {
+ ConnectPoint cpOut = outPort.realizedBy();
+ if (cpOut != null) {
+ TrafficTreatment treatment = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(cpOut.port()).build();
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(
+ cpOut.deviceId(), treatment, packet.data());
+ outboundPackets.add(outboundPacket);
+ } else {
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ outPort.number(), networkId, packet.sendThrough());
+ }
+ }
+ }
+ }
+
+ return outboundPackets;
+ }
+
+ /**
+ * Translate the requested virtual packet context into
+ * a set of physical outbound packets.
+ *
+ * @param context A handled virtual packet context
+ */
+ private Set<OutboundPacket> devirtualizeContext(VirtualPacketContext context) {
+
+ Set<OutboundPacket> outboundPackets = new HashSet<>();
+
+ NetworkId networkId = context.networkId();
+ TrafficTreatment vTreatment = context.treatmentBuilder().build();
+ DeviceId sendThrough = context.outPacket().sendThrough();
+
+ Set<VirtualPort> vPorts = vnaService
+ .getVirtualPorts(networkId, sendThrough);
+
+ PortNumber vOutPortNum = vTreatment.allInstructions().stream()
+ .filter(i -> i.type() == Instruction.Type.OUTPUT)
+ .map(i -> ((Instructions.OutputInstruction) i).port())
+ .findFirst().get();
+
+ TrafficTreatment.Builder commonTreatmentBuilder
+ = DefaultTrafficTreatment.builder();
+ vTreatment.allInstructions().stream()
+ .filter(i -> i.type() != Instruction.Type.OUTPUT)
+ .forEach(i -> commonTreatmentBuilder.add(i));
+ TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
+
+ if (!vOutPortNum.isLogical()) {
+ Optional<ConnectPoint> optionalCpOut = vPorts.stream()
+ .filter(v -> v.number().equals(vOutPortNum))
+ .map(v -> v.realizedBy())
+ .findFirst();
+ if (!optionalCpOut.isPresent()) {
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ vOutPortNum, networkId, sendThrough);
+ return outboundPackets;
+ }
+ ConnectPoint egressPoint = optionalCpOut.get();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(egressPoint.port()).build();
+
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(
+ egressPoint.deviceId(), treatment, context.outPacket().data());
+ outboundPackets.add(outboundPacket);
+ } else {
+ if (vOutPortNum == PortNumber.FLOOD) {
+ Set<VirtualPort> outPorts = vPorts.stream()
+ .filter(vp -> !vp.number().isLogical())
+ .filter(vp -> vp.number() !=
+ context.inPacket().receivedFrom().port())
+ .collect(Collectors.toSet());
+
+ for (VirtualPort outPort : outPorts) {
+ ConnectPoint cpOut = outPort.realizedBy();
+ if (cpOut != null) {
+ TrafficTreatment treatment = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(cpOut.port()).build();
+ OutboundPacket outboundPacket = new DefaultOutboundPacket(
+ cpOut.deviceId(), treatment, context.outPacket().data());
+ outboundPackets.add(outboundPacket);
+ } else {
+ log.warn("Port {} is not realized yet, in Network {}, Device {}",
+ outPort.number(), networkId, sendThrough);
+ }
+ }
+ }
+ }
+ return outboundPackets;
+ }
+
+ private final class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ }
+ VirtualPacketContext vContexts = virtualize(context);
+
+ if (vContexts == null) {
+ return;
+ }
+
+ VirtualPacketProviderService service =
+ (VirtualPacketProviderService) providerRegistryService
+ .getProviderService(vContexts.networkId(),
+ VirtualPacketProvider.class);
+ if (service != null) {
+ service.processPacket(vContexts);
+ }
+ }
+ }
+
+ private class InternalVirtualNetworkListener implements VirtualNetworkListener {
+
+ @Override
+ public void event(VirtualNetworkEvent event) {
+ switch (event.type()) {
+ case NETWORK_ADDED:
+ if (networkIdSet.isEmpty()) {
+ processor = new InternalPacketProcessor();
+ packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
+ log.info("Packet processor {} for virtual network is added.", processor.getClass().getName());
+ }
+ networkIdSet.add(event.subject());
+ break;
+
+ case NETWORK_REMOVED:
+ networkIdSet.remove(event.subject());
+ if (networkIdSet.isEmpty()) {
+ packetService.removeProcessor(processor);
+ log.info("Packet processor {} for virtual network is removed.", processor.getClass().getName());
+ processor = null;
+ }
+ break;
+
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java
new file mode 100644
index 0000000..ebd87ea
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java
@@ -0,0 +1,168 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.VirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Implementation of the virtual provider registry and providerService registry service.
+ */
+@Component(service = VirtualProviderRegistryService.class)
+public class VirtualProviderManager implements VirtualProviderRegistryService {
+
+ private final Map<ProviderId, VirtualProvider> providers = new HashMap<>();
+ private final Map<ProviderId, VirtualProviderService> servicesWithProvider = new HashMap<>();
+ private final Map<String, VirtualProvider> providersByScheme = new HashMap<>();
+ private final Map<NetworkId, Set<VirtualProviderService>> servicesByNetwork = new HashMap<>();
+ private static Logger log = LoggerFactory.getLogger(VirtualProviderManager.class);
+
+ @Override
+ public synchronized void registerProvider(VirtualProvider virtualProvider) {
+ checkNotNull(virtualProvider, "Provider cannot be null");
+ checkState(!providers.containsKey(virtualProvider.id()),
+ "Provider %s already registered", virtualProvider.id());
+
+ // If the provider is a primary one, check for a conflict.
+ ProviderId pid = virtualProvider.id();
+ checkState(pid.isAncillary() || !providersByScheme.containsKey(pid.scheme()),
+ "A primary provider with id %s is already registered",
+ providersByScheme.get(pid.scheme()));
+
+ providers.put(virtualProvider.id(), virtualProvider);
+
+ // Register the provider by URI scheme only if it is not ancillary.
+ if (!pid.isAncillary()) {
+ providersByScheme.put(pid.scheme(), virtualProvider);
+ }
+ }
+
+ @Override
+ public synchronized void unregisterProvider(VirtualProvider virtualProvider) {
+ checkNotNull(virtualProvider, "Provider cannot be null");
+
+ //TODO: invalidate provider services which subscribe the provider
+ providers.remove(virtualProvider.id());
+
+ if (!virtualProvider.id().isAncillary()) {
+ providersByScheme.remove(virtualProvider.id().scheme());
+ }
+ }
+
+ @Override
+ public synchronized void
+ registerProviderService(NetworkId networkId,
+ VirtualProviderService virtualProviderService) {
+ Set<VirtualProviderService> services =
+ servicesByNetwork.computeIfAbsent(networkId, k -> new HashSet<>());
+
+ services.add(virtualProviderService);
+ }
+
+ @Override
+ public synchronized void
+ unregisterProviderService(NetworkId networkId,
+ VirtualProviderService virtualProviderService) {
+ Set<VirtualProviderService> services = servicesByNetwork.get(networkId);
+
+ if (services != null) {
+ services.remove(virtualProviderService);
+ }
+ }
+
+ @Override
+ public synchronized Set<ProviderId> getProviders() {
+ return ImmutableSet.copyOf(providers.keySet());
+ }
+
+ @Override
+ public Set<ProviderId> getProvidersByService(VirtualProviderService
+ virtualProviderService) {
+ Class clazz = getProviderClass(virtualProviderService);
+
+ return ImmutableSet.copyOf(providers.values().stream()
+ .filter(clazz::isInstance)
+ .map(VirtualProvider::id)
+ .collect(Collectors.toSet()));
+ }
+
+ @Override
+ public synchronized VirtualProvider getProvider(ProviderId providerId) {
+ return providers.get(providerId);
+ }
+
+ @Override
+ public synchronized VirtualProvider getProvider(DeviceId deviceId) {
+ return providersByScheme.get(deviceId.uri().getScheme());
+ }
+
+ @Override
+ public synchronized VirtualProvider getProvider(String scheme) {
+ return providersByScheme.get(scheme);
+ }
+
+ @Override
+ public synchronized VirtualProviderService
+ getProviderService(NetworkId networkId, Class<? extends VirtualProvider> providerClass) {
+ Set<VirtualProviderService> services = servicesByNetwork.get(networkId);
+
+ if (services == null) {
+ return null;
+ }
+
+ return services.stream()
+ .filter(s -> getProviderClass(s).equals(providerClass))
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Returns the class type of parameter type.
+ * More specifically, it returns the class type of provider service's provider type.
+ *
+ * @param service a virtual provider service
+ * @return the class type of provider service of the service
+ */
+ private Class getProviderClass(VirtualProviderService service) {
+ String className = service.getClass().getGenericSuperclass().getTypeName();
+ String pramType = className.split("<")[1].split(">")[0];
+
+ try {
+ return Class.forName(pramType);
+ } catch (ClassNotFoundException e) {
+ log.warn("getProviderClass()", e);
+ }
+
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/package-info.java
new file mode 100644
index 0000000..1d9646f
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/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.
+ */
+
+/**
+ * Network virtualization provider implementations.
+ */
+package org.onosproject.incubator.net.virtual.impl.provider;
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/TenantWebResource.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/TenantWebResource.java
new file mode 100644
index 0000000..768c3b9
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/TenantWebResource.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2018-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.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * Query and manage tenants of virtual networks.
+ */
+@Path("tenants")
+public class TenantWebResource extends AbstractWebResource {
+
+ private static final String MISSING_TENANTID = "Missing tenant identifier";
+ private static final String TENANTID_NOT_FOUND = "Tenant identifier not found";
+ private static final String INVALID_TENANTID = "Invalid tenant identifier ";
+
+ @Context
+ private UriInfo uriInfo;
+
+ private final VirtualNetworkAdminService vnetAdminService = get(VirtualNetworkAdminService.class);
+
+ /**
+ * Returns all tenant identifiers.
+ *
+ * @return 200 OK with set of tenant identifiers
+ * @onos.rsModel TenantIds
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getVirtualNetworkTenantIds() {
+ Iterable<TenantId> tenantIds = vnetAdminService.getTenantIds();
+ return ok(encodeArray(TenantId.class, "tenants", tenantIds)).build();
+ }
+
+ /**
+ * Creates a tenant with the given tenant identifier.
+ *
+ * @param stream TenantId JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel TenantId
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response addTenantId(InputStream stream) {
+ try {
+ final TenantId tid = getTenantIdFromJsonStream(stream);
+ vnetAdminService.registerTenantId(tid);
+ final TenantId resultTid = getExistingTenantId(vnetAdminService, tid);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("tenants")
+ .path(resultTid.id());
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the specified tenant with the specified tenant identifier.
+ *
+ * @param tenantId tenant identifier
+ * @return 204 NO CONTENT
+ */
+ @DELETE
+ @Path("{tenantId}")
+ public Response removeTenantId(@PathParam("tenantId") String tenantId) {
+ final TenantId tid = TenantId.tenantId(tenantId);
+ final TenantId existingTid = getExistingTenantId(vnetAdminService, tid);
+ vnetAdminService.unregisterTenantId(existingTid);
+ return Response.noContent().build();
+ }
+
+ /**
+ * Gets the tenant identifier from the JSON stream.
+ *
+ * @param stream TenantId JSON stream
+ * @return TenantId
+ * @throws IOException if unable to parse the request
+ */
+ private TenantId getTenantIdFromJsonStream(InputStream stream) throws IOException {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode specifiedTenantId = jsonTree.get("id");
+
+ if (specifiedTenantId == null) {
+ throw new IllegalArgumentException(MISSING_TENANTID);
+ }
+ return TenantId.tenantId(specifiedTenantId.asText());
+ }
+
+ /**
+ * Get the matching tenant identifier from existing tenant identifiers in system.
+ *
+ * @param vnetAdminSvc virtual network administration service
+ * @param tidIn tenant identifier
+ * @return TenantId
+ */
+ protected static TenantId getExistingTenantId(VirtualNetworkAdminService vnetAdminSvc,
+ TenantId tidIn) {
+ return vnetAdminSvc
+ .getTenantIds()
+ .stream()
+ .filter(tenantId -> tenantId.equals(tidIn))
+ .findFirst()
+ .orElseThrow(() -> new ItemNotFoundException(TENANTID_NOT_FOUND));
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebApplication.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebApplication.java
new file mode 100644
index 0000000..4a80dbe
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018-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.rest;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * Virtual network REST APIs web application.
+ */
+public class VirtualNetworkWebApplication extends AbstractWebApplication {
+ @Override
+ public Set<Class<?>> getClasses() {
+ return getClasses(
+ TenantWebResource.class,
+ VirtualNetworkWebResource.class
+ );
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResource.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResource.java
new file mode 100644
index 0000000..e8696c3
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResource.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2018-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.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+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.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * Query and Manage Virtual Network elements.
+ */
+@Path("vnets")
+public class VirtualNetworkWebResource extends AbstractWebResource {
+
+ private static final String MISSING_FIELD = "Missing ";
+ private static final String INVALID_FIELD = "Invalid ";
+
+ private final VirtualNetworkAdminService vnetAdminService = get(VirtualNetworkAdminService.class);
+ private final VirtualNetworkService vnetService = get(VirtualNetworkService.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ // VirtualNetwork
+
+ /**
+ * Returns all virtual networks.
+ *
+ * @return 200 OK with set of virtual networks
+ * @onos.rsModel VirtualNetworks
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getVirtualNetworks() {
+ Set<TenantId> tenantIds = vnetAdminService.getTenantIds();
+ List<VirtualNetwork> allVnets = tenantIds.stream()
+ .map(tenantId -> vnetService.getVirtualNetworks(tenantId))
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ return ok(encodeArray(VirtualNetwork.class, "vnets", allVnets)).build();
+ }
+
+ /**
+ * Returns the virtual networks with the specified tenant identifier.
+ *
+ * @param tenantId tenant identifier
+ * @return 200 OK with a virtual network, 404 not found
+ * @onos.rsModel VirtualNetworks
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{tenantId}")
+ public Response getVirtualNetworkById(@PathParam("tenantId") String tenantId) {
+ final TenantId existingTid = TenantWebResource.getExistingTenantId(vnetAdminService,
+ TenantId.tenantId(tenantId));
+ Set<VirtualNetwork> vnets = vnetService.getVirtualNetworks(existingTid);
+ return ok(encodeArray(VirtualNetwork.class, "vnets", vnets)).build();
+ }
+
+ /**
+ * Creates a virtual network from the JSON input stream.
+ *
+ * @param stream tenant identifier JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel TenantId
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createVirtualNetwork(InputStream stream) {
+ try {
+ final TenantId tid = TenantId.tenantId(getFromJsonStream(stream, "id").asText());
+ VirtualNetwork newVnet = vnetAdminService.createVirtualNetwork(tid);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("vnets")
+ .path(newVnet.id().toString());
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the virtual network with the specified network identifier.
+ *
+ * @param networkId network identifier
+ * @return 204 NO CONTENT
+ */
+ @DELETE
+ @Path("{networkId}")
+ public Response removeVirtualNetwork(@PathParam("networkId") long networkId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ vnetAdminService.removeVirtualNetwork(nid);
+ return Response.noContent().build();
+ }
+
+ // VirtualDevice
+
+ /**
+ * Returns all virtual network devices in a virtual network.
+ *
+ * @param networkId network identifier
+ * @return 200 OK with set of virtual devices, 404 not found
+ * @onos.rsModel VirtualDevices
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{networkId}/devices")
+ public Response getVirtualDevices(@PathParam("networkId") long networkId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ Set<VirtualDevice> vdevs = vnetService.getVirtualDevices(nid);
+ return ok(encodeArray(VirtualDevice.class, "devices", vdevs)).build();
+ }
+
+ /**
+ * Creates a virtual device from the JSON input stream.
+ *
+ * @param networkId network identifier
+ * @param stream virtual device JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel VirtualDevice
+ */
+ @POST
+ @Path("{networkId}/devices")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createVirtualDevice(@PathParam("networkId") long networkId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ final VirtualDevice vdevReq = codec(VirtualDevice.class).decode(jsonTree, this);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ final VirtualDevice vdevRes = vnetAdminService.createVirtualDevice(vdevReq.networkId(),
+ vdevReq.id());
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("vnets").path(specifiedNetworkId.asText())
+ .path("devices").path(vdevRes.id().toString());
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the virtual network device from the virtual network.
+ *
+ * @param networkId network identifier
+ * @param deviceId device identifier
+ * @return 204 NO CONTENT
+ */
+ @DELETE
+ @Path("{networkId}/devices/{deviceId}")
+ public Response removeVirtualDevice(@PathParam("networkId") long networkId,
+ @PathParam("deviceId") String deviceId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ DeviceId did = DeviceId.deviceId(deviceId);
+ vnetAdminService.removeVirtualDevice(nid, did);
+ return Response.noContent().build();
+ }
+
+ // VirtualPort
+
+ /**
+ * Returns all virtual network ports in a virtual device in a virtual network.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device identifier
+ * @return 200 OK with set of virtual ports, 404 not found
+ * @onos.rsModel VirtualPorts
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{networkId}/devices/{deviceId}/ports")
+ public Response getVirtualPorts(@PathParam("networkId") long networkId,
+ @PathParam("deviceId") String deviceId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ Iterable<VirtualPort> vports = vnetService.getVirtualPorts(nid, DeviceId.deviceId(deviceId));
+ return ok(encodeArray(VirtualPort.class, "ports", vports)).build();
+ }
+
+ /**
+ * Creates a virtual network port in a virtual device in a virtual network.
+ *
+ * @param networkId network identifier
+ * @param virtDeviceId virtual device identifier
+ * @param stream virtual port JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel VirtualPort
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{networkId}/devices/{deviceId}/ports")
+ public Response createVirtualPort(@PathParam("networkId") long networkId,
+ @PathParam("deviceId") String virtDeviceId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+// final VirtualPort vportReq = codec(VirtualPort.class).decode(jsonTree, this);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ JsonNode specifiedDeviceId = jsonTree.get("deviceId");
+ if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ if (specifiedDeviceId == null || !specifiedDeviceId.asText().equals(virtDeviceId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "deviceId");
+ }
+ JsonNode specifiedPortNum = jsonTree.get("portNum");
+ JsonNode specifiedPhysDeviceId = jsonTree.get("physDeviceId");
+ JsonNode specifiedPhysPortNum = jsonTree.get("physPortNum");
+ final NetworkId nid = NetworkId.networkId(networkId);
+ DeviceId vdevId = DeviceId.deviceId(virtDeviceId);
+
+ ConnectPoint realizedBy = new ConnectPoint(DeviceId.deviceId(specifiedPhysDeviceId.asText()),
+ PortNumber.portNumber(specifiedPhysPortNum.asText()));
+ VirtualPort vport = vnetAdminService.createVirtualPort(nid, vdevId,
+ PortNumber.portNumber(specifiedPortNum.asText()), realizedBy);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("vnets").path(specifiedNetworkId.asText())
+ .path("devices").path(specifiedDeviceId.asText())
+ .path("ports").path(vport.number().toString());
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the virtual network port from the virtual device in a virtual network.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device identifier
+ * @param portNum virtual port number
+ * @return 204 NO CONTENT
+ */
+ @DELETE
+ @Path("{networkId}/devices/{deviceId}/ports/{portNum}")
+ public Response removeVirtualPort(@PathParam("networkId") long networkId,
+ @PathParam("deviceId") String deviceId,
+ @PathParam("portNum") long portNum) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ vnetAdminService.removeVirtualPort(nid, DeviceId.deviceId(deviceId),
+ PortNumber.portNumber(portNum));
+ return Response.noContent().build();
+ }
+
+ // VirtualLink
+
+ /**
+ * Returns all virtual network links in a virtual network.
+ *
+ * @param networkId network identifier
+ * @return 200 OK with set of virtual network links
+ * @onos.rsModel VirtualLinks
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{networkId}/links")
+ public Response getVirtualLinks(@PathParam("networkId") long networkId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ Set<VirtualLink> vlinks = vnetService.getVirtualLinks(nid);
+ return ok(encodeArray(VirtualLink.class, "links", vlinks)).build();
+ }
+
+ /**
+ * Creates a virtual network link from the JSON input stream.
+ *
+ * @param networkId network identifier
+ * @param stream virtual link JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel VirtualLink
+ */
+ @POST
+ @Path("{networkId}/links")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createVirtualLink(@PathParam("networkId") long networkId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ final VirtualLink vlinkReq = codec(VirtualLink.class).decode(jsonTree, this);
+ vnetAdminService.createVirtualLink(vlinkReq.networkId(),
+ vlinkReq.src(), vlinkReq.dst());
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("vnets").path(specifiedNetworkId.asText())
+ .path("links");
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the virtual network link from the JSON input stream.
+ *
+ * @param networkId network identifier
+ * @param stream virtual link JSON stream
+ * @return 204 NO CONTENT
+ * @onos.rsModel VirtualLink
+ */
+ @DELETE
+ @Path("{networkId}/links")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response removeVirtualLink(@PathParam("networkId") long networkId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ if (specifiedNetworkId != null &&
+ specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ final VirtualLink vlinkReq = codec(VirtualLink.class).decode(jsonTree, this);
+ vnetAdminService.removeVirtualLink(vlinkReq.networkId(),
+ vlinkReq.src(), vlinkReq.dst());
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ return Response.noContent().build();
+ }
+
+ /**
+ * Returns all virtual network hosts in a virtual network.
+ *
+ * @param networkId network identifier
+ * @return 200 OK with set of virtual network hosts
+ * @onos.rsModel VirtualHosts
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{networkId}/hosts")
+ public Response getVirtualHosts(@PathParam("networkId") long networkId) {
+ NetworkId nid = NetworkId.networkId(networkId);
+ Set<VirtualHost> vhosts = vnetService.getVirtualHosts(nid);
+ return ok(encodeArray(VirtualHost.class, "hosts", vhosts)).build();
+ }
+
+ /**
+ * Creates a virtual network host from the JSON input stream.
+ *
+ * @param networkId network identifier
+ * @param stream virtual host JSON stream
+ * @return status of the request - CREATED if the JSON is correct,
+ * BAD_REQUEST if the JSON is invalid
+ * @onos.rsModel VirtualHostPut
+ */
+ @POST
+ @Path("{networkId}/hosts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createVirtualHost(@PathParam("networkId") long networkId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ final VirtualHost vhostReq = codec(VirtualHost.class).decode(jsonTree, this);
+ vnetAdminService.createVirtualHost(vhostReq.networkId(), vhostReq.id(),
+ vhostReq.mac(), vhostReq.vlan(),
+ vhostReq.location(), vhostReq.ipAddresses());
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path("vnets").path(specifiedNetworkId.asText())
+ .path("hosts");
+ return Response
+ .created(locationBuilder.build())
+ .build();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Removes the virtual network host from the JSON input stream.
+ *
+ * @param networkId network identifier
+ * @param stream virtual host JSON stream
+ * @return 204 NO CONTENT
+ * @onos.rsModel VirtualHost
+ */
+ @DELETE
+ @Path("{networkId}/hosts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response removeVirtualHost(@PathParam("networkId") long networkId,
+ InputStream stream) {
+ try {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode specifiedNetworkId = jsonTree.get("networkId");
+ if (specifiedNetworkId != null &&
+ specifiedNetworkId.asLong() != (networkId)) {
+ throw new IllegalArgumentException(INVALID_FIELD + "networkId");
+ }
+ final VirtualHost vhostReq = codec(VirtualHost.class).decode(jsonTree, this);
+ vnetAdminService.removeVirtualHost(vhostReq.networkId(), vhostReq.id());
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ return Response.noContent().build();
+ }
+
+ /**
+ * Get the tenant identifier from the JSON stream.
+ *
+ * @param stream TenantId JSON stream
+ * @param jsonFieldName field name
+ * @return JsonNode
+ * @throws IOException if unable to parse the request
+ */
+ private JsonNode getFromJsonStream(InputStream stream, String jsonFieldName) throws IOException {
+ ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
+ JsonNode jsonNode = jsonTree.get(jsonFieldName);
+
+ if (jsonNode == null) {
+ throw new IllegalArgumentException(MISSING_FIELD + jsonFieldName);
+ }
+ return jsonNode;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/package-info.java
new file mode 100644
index 0000000..0b8a711
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/rest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * REST API of the virtual network subsystem.
+ */
+package org.onosproject.incubator.net.virtual.rest;
\ No newline at end of file
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/AbstractVirtualStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/AbstractVirtualStore.java
new file mode 100644
index 0000000..73f5bdb
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/AbstractVirtualStore.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.store.impl;
+
+import com.google.common.collect.Maps;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualStore;
+import org.onosproject.store.StoreDelegate;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Base implementation of a virtual store.
+ */
+public class AbstractVirtualStore<E extends Event, D extends StoreDelegate<E>>
+ implements VirtualStore<E, D> {
+
+ protected Map<NetworkId, D> delegateMap = Maps.newConcurrentMap();
+
+ @Override
+ public void setDelegate(NetworkId networkId, D delegate) {
+ checkState(delegateMap.get(networkId) == null
+ || delegateMap.get(networkId) == delegate,
+ "Store delegate already set");
+
+ delegateMap.putIfAbsent(networkId, delegate);
+ }
+
+ @Override
+ public void unsetDelegate(NetworkId networkId, D delegate) {
+ if (delegateMap.get(networkId) == delegate) {
+ delegateMap.remove(networkId, delegate);
+ }
+ }
+
+ @Override
+ public boolean hasDelegate(NetworkId networkId) {
+ return delegateMap.get(networkId) != null;
+ }
+
+ /**
+ * Notifies the delegate with the specified event.
+ *
+ * @param networkId a virtual network identifier
+ * @param event event to delegate
+ */
+ protected void notifyDelegate(NetworkId networkId, E event) {
+ if (delegateMap.get(networkId) != null) {
+ delegateMap.get(networkId).notify(event);
+ }
+ }
+
+ /**
+ * Notifies the delegate with the specified list of events.
+ *
+ * @param networkId a virtual network identifier
+ * @param events list of events to delegate
+ */
+ protected void notifyDelegate(NetworkId networkId, List<E> events) {
+ for (E event: events) {
+ notifyDelegate(networkId, event);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/ConsistentVirtualDeviceMastershipStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/ConsistentVirtualDeviceMastershipStore.java
new file mode 100644
index 0000000..408ce9c
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/ConsistentVirtualDeviceMastershipStore.java
@@ -0,0 +1,467 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.LeadershipAdminService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMastershipStore;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipInfo;
+import org.onosproject.mastership.MastershipStoreDelegate;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.mastership.MastershipEvent.Type.BACKUPS_CHANGED;
+import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
+import static org.onosproject.mastership.MastershipEvent.Type.SUSPENDED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true, enabled = false, service = VirtualNetworkMastershipStore.class)
+public class ConsistentVirtualDeviceMastershipStore
+ extends AbstractVirtualStore<MastershipEvent, MastershipStoreDelegate>
+ implements VirtualNetworkMastershipStore {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipAdminService leadershipAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ private NodeId localNodeId;
+
+ private static final MessageSubject ROLE_RELINQUISH_SUBJECT =
+ new MessageSubject("virtual-mastership-store-device-role-relinquish");
+
+ private static final Pattern DEVICE_MASTERSHIP_TOPIC_PATTERN =
+ Pattern.compile("vnet:(.*),device:(.*)");
+
+ private ExecutorService eventHandler;
+ private ExecutorService messageHandlingExecutor;
+ private ScheduledExecutorService transferExecutor;
+ private final LeadershipEventListener leadershipEventListener =
+ new InternalDeviceMastershipEventListener();
+
+ private static final String NODE_ID_NULL = "Node ID cannot be null";
+ private static final String NETWORK_ID_NULL = "Network ID cannot be null";
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final int WAIT_BEFORE_MASTERSHIP_HANDOFF_MILLIS = 3000;
+
+ public static final Serializer SERIALIZER = Serializer.using(
+ KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(MastershipRole.class)
+ .register(MastershipEvent.class)
+ .register(MastershipEvent.Type.class)
+ .register(VirtualDeviceId.class)
+ .build("VirtualMastershipStore"));
+
+ @Activate
+ public void activate() {
+ eventHandler = Executors.newSingleThreadExecutor(
+ groupedThreads("onos/store/virtual/mastership", "event-handler", log));
+
+ messageHandlingExecutor =
+ Executors.newSingleThreadExecutor(
+ groupedThreads("onos/store/virtual/mastership", "message-handler", log));
+ transferExecutor =
+ Executors.newSingleThreadScheduledExecutor(
+ groupedThreads("onos/store/virtual/mastership", "mastership-transfer-executor", log));
+ clusterCommunicator.addSubscriber(ROLE_RELINQUISH_SUBJECT,
+ SERIALIZER::decode,
+ this::relinquishLocalRole,
+ SERIALIZER::encode,
+ messageHandlingExecutor);
+ localNodeId = clusterService.getLocalNode().id();
+ leadershipService.addListener(leadershipEventListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ clusterCommunicator.removeSubscriber(ROLE_RELINQUISH_SUBJECT);
+ leadershipService.removeListener(leadershipEventListener);
+ messageHandlingExecutor.shutdown();
+ transferExecutor.shutdown();
+ eventHandler.shutdown();
+ log.info("Stopped");
+ }
+
+ @Override
+ public CompletableFuture<MastershipRole> requestRole(NetworkId networkId,
+ DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ Leadership leadership = leadershipService.runForLeadership(leadershipTopic);
+ return CompletableFuture
+ .completedFuture(localNodeId.equals(leadership.leaderNodeId()) ?
+ MastershipRole.MASTER : MastershipRole.STANDBY);
+ }
+
+ @Override
+ public MastershipRole getRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(nodeId != null, NODE_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ Leadership leadership = leadershipService.getLeadership(leadershipTopic);
+ NodeId leader = leadership == null ? null : leadership.leaderNodeId();
+ List<NodeId> candidates = leadership == null ?
+ ImmutableList.of() : ImmutableList.copyOf(leadership.candidates());
+ return Objects.equal(nodeId, leader) ?
+ MastershipRole.MASTER : candidates.contains(nodeId) ?
+ MastershipRole.STANDBY : MastershipRole.NONE;
+ }
+
+ @Override
+ public NodeId getMaster(NetworkId networkId, DeviceId deviceId) {
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ return leadershipService.getLeader(createDeviceMastershipTopic(networkId, deviceId));
+ }
+
+ @Override
+ public RoleInfo getNodes(NetworkId networkId, DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+ Leadership leadership = leadershipService.getLeadership(createDeviceMastershipTopic(networkId, deviceId));
+ return new RoleInfo(leadership.leaderNodeId(), leadership.candidates());
+ }
+
+ @Override
+ public MastershipInfo getMastership(NetworkId networkId, DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+ Leadership leadership = leadershipService.getLeadership(createDeviceMastershipTopic(networkId, deviceId));
+ return buildMastershipFromLeadership(leadership);
+ }
+
+ @Override
+ public Set<DeviceId> getDevices(NetworkId networkId, NodeId nodeId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(nodeId != null, NODE_ID_NULL);
+
+ // FIXME This result contains REMOVED device.
+ // MastershipService cannot listen to DeviceEvent to GC removed topic,
+ // since DeviceManager depend on it.
+ // Reference count, etc. at LeadershipService layer?
+ return leadershipService
+ .ownedTopics(nodeId)
+ .stream()
+ .filter(this::isVirtualMastershipTopic)
+ .map(this::extractDeviceIdFromTopic)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public CompletableFuture<MastershipEvent> setMaster(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(nodeId != null, NODE_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ if (leadershipAdminService.promoteToTopOfCandidateList(leadershipTopic, nodeId)) {
+ transferExecutor.schedule(() -> leadershipAdminService.transferLeadership(leadershipTopic, nodeId),
+ WAIT_BEFORE_MASTERSHIP_HANDOFF_MILLIS, TimeUnit.MILLISECONDS);
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public MastershipTerm getTermFor(NetworkId networkId, DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ Leadership leadership = leadershipService.getLeadership(leadershipTopic);
+ return leadership != null && leadership.leaderNodeId() != null ?
+ MastershipTerm.of(leadership.leaderNodeId(),
+ leadership.leader().term()) : null;
+ }
+
+ @Override
+ public CompletableFuture<MastershipEvent> setStandby(NetworkId networkId,
+ NodeId nodeId,
+ DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(nodeId != null, NODE_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ NodeId currentMaster = getMaster(networkId, deviceId);
+ if (!nodeId.equals(currentMaster)) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ List<NodeId> candidates = leadershipService.getCandidates(leadershipTopic);
+
+ NodeId newMaster = candidates.stream()
+ .filter(candidate -> !Objects.equal(nodeId, candidate))
+ .findFirst()
+ .orElse(null);
+ log.info("Transitioning to role {} for {}. Next master: {}",
+ newMaster != null ? MastershipRole.STANDBY : MastershipRole.NONE,
+ deviceId, newMaster);
+
+ if (newMaster != null) {
+ return setMaster(networkId, newMaster, deviceId);
+ }
+ return relinquishRole(networkId, nodeId, deviceId);
+ }
+
+ @Override
+ public CompletableFuture<MastershipEvent> relinquishRole(NetworkId networkId,
+ NodeId nodeId,
+ DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(nodeId != null, NODE_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ if (nodeId.equals(localNodeId)) {
+ return relinquishLocalRoleByNetwork(networkId, deviceId);
+ }
+
+ log.debug("Forwarding request to relinquish "
+ + "role for vnet {} device {} to {}", deviceId, nodeId);
+ return clusterCommunicator.sendAndReceive(
+ new VirtualDeviceId(networkId, deviceId),
+ ROLE_RELINQUISH_SUBJECT,
+ SERIALIZER::encode,
+ SERIALIZER::decode,
+ nodeId);
+ }
+
+ private CompletableFuture<MastershipEvent> relinquishLocalRoleByNetwork(NetworkId networkId,
+ DeviceId deviceId) {
+ checkArgument(networkId != null, NETWORK_ID_NULL);
+ checkArgument(deviceId != null, DEVICE_ID_NULL);
+
+ String leadershipTopic = createDeviceMastershipTopic(networkId, deviceId);
+ if (!leadershipService.getCandidates(leadershipTopic).contains(localNodeId)) {
+ return CompletableFuture.completedFuture(null);
+ }
+ MastershipEvent.Type eventType = localNodeId.equals(leadershipService.getLeader(leadershipTopic)) ?
+ MastershipEvent.Type.MASTER_CHANGED : MastershipEvent.Type.BACKUPS_CHANGED;
+ leadershipService.withdraw(leadershipTopic);
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(eventType, deviceId, getMastership(networkId, deviceId)));
+ }
+
+ private CompletableFuture<MastershipEvent>
+ relinquishLocalRole(VirtualDeviceId virtualDeviceId) {
+ return relinquishLocalRoleByNetwork(virtualDeviceId.networkId,
+ virtualDeviceId.deviceId);
+ }
+
+ @Override
+ public void relinquishAllRole(NetworkId networkId, NodeId nodeId) {
+ // Noop. LeadershipService already takes care of detecting and purging stale locks.
+ }
+
+ private MastershipInfo buildMastershipFromLeadership(Leadership leadership) {
+ ImmutableMap.Builder<NodeId, MastershipRole> builder = ImmutableMap.builder();
+ if (leadership.leaderNodeId() != null) {
+ builder.put(leadership.leaderNodeId(), MastershipRole.MASTER);
+ }
+ leadership.candidates().forEach(nodeId -> builder.put(nodeId, MastershipRole.STANDBY));
+ clusterService.getNodes().stream()
+ .filter(node -> !leadership.candidates().contains(node.id()))
+ .forEach(node -> builder.put(node.id(), MastershipRole.NONE));
+
+ return new MastershipInfo(
+ leadership.leader() != null ? leadership.leader().term() : 0,
+ leadership.leader() != null
+ ? Optional.of(leadership.leader().nodeId())
+ : Optional.empty(),
+ builder.build());
+ }
+
+ private class InternalDeviceMastershipEventListener
+ implements LeadershipEventListener {
+
+ @Override
+ public boolean isRelevant(LeadershipEvent event) {
+ Leadership leadership = event.subject();
+ return isVirtualMastershipTopic(leadership.topic());
+ }
+
+ @Override
+ public void event(LeadershipEvent event) {
+ eventHandler.execute(() -> handleEvent(event));
+ }
+
+ private void handleEvent(LeadershipEvent event) {
+ Leadership leadership = event.subject();
+
+ NetworkId networkId = extractNetworkIdFromTopic(leadership.topic());
+ DeviceId deviceId = extractDeviceIdFromTopic(leadership.topic());
+ MastershipInfo mastershipInfo = event.type() != LeadershipEvent.Type.SERVICE_DISRUPTED
+ ? buildMastershipFromLeadership(event.subject())
+ : new MastershipInfo();
+
+ switch (event.type()) {
+ case LEADER_AND_CANDIDATES_CHANGED:
+ notifyDelegate(networkId, new MastershipEvent(BACKUPS_CHANGED, deviceId, mastershipInfo));
+ notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId, mastershipInfo));
+ break;
+ case LEADER_CHANGED:
+ notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId, mastershipInfo));
+ break;
+ case CANDIDATES_CHANGED:
+ notifyDelegate(networkId, new MastershipEvent(BACKUPS_CHANGED, deviceId, mastershipInfo));
+ break;
+ case SERVICE_DISRUPTED:
+ notifyDelegate(networkId, new MastershipEvent(SUSPENDED, deviceId, mastershipInfo));
+ break;
+ case SERVICE_RESTORED:
+ // Do nothing, wait for updates from peers
+ break;
+ default:
+ }
+ }
+ }
+
+ private String createDeviceMastershipTopic(NetworkId networkId, DeviceId deviceId) {
+ return String.format("vnet:%s,device:%s", networkId.toString(), deviceId.toString());
+ }
+
+ /**
+ * Returns the virtual network identifier extracted from the topic.
+ *
+ * @param topic topic to extract virtual network identifier
+ * @return an extracted virtual network identifier
+ * @throws IllegalArgumentException the topic not match with the pattern
+ * used for virtual network mastership store
+ */
+ private NetworkId extractNetworkIdFromTopic(String topic) {
+ Matcher m = DEVICE_MASTERSHIP_TOPIC_PATTERN.matcher(topic);
+ if (m.matches()) {
+ return NetworkId.networkId(Long.getLong(m.group(1)));
+ } else {
+ throw new IllegalArgumentException("Invalid virtual mastership topic: "
+ + topic);
+ }
+ }
+
+ /**
+ * Returns the device identifier extracted from the topic.
+ *
+ * @param topic topic to extract device identifier
+ * @return an extracted virtual device identifier
+ * @throws IllegalArgumentException the topic not match with the pattern
+ * used for virtual network mastership store
+ */
+ private DeviceId extractDeviceIdFromTopic(String topic) {
+ Matcher m = DEVICE_MASTERSHIP_TOPIC_PATTERN.matcher(topic);
+ if (m.matches()) {
+ return DeviceId.deviceId(m.group(2));
+ } else {
+ throw new IllegalArgumentException("Invalid virtual mastership topic: "
+ + topic);
+ }
+ }
+
+ /**
+ * Returns whether the topic is matched with virtual mastership store topic.
+ *
+ * @param topic topic to match
+ * @return True when the topic matched with virtual network mastership store
+ */
+ private boolean isVirtualMastershipTopic(String topic) {
+ Matcher m = DEVICE_MASTERSHIP_TOPIC_PATTERN.matcher(topic);
+ return m.matches();
+ }
+
+ /**
+ * A wrapper class used for the communication service.
+ */
+ private class VirtualDeviceId {
+ NetworkId networkId;
+ DeviceId deviceId;
+
+ public VirtualDeviceId(NetworkId networkId, DeviceId deviceId) {
+ this.networkId = networkId;
+ this.deviceId = deviceId;
+ }
+
+ public int hashCode() {
+ return Objects.hashCode(networkId, deviceId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof VirtualDeviceId) {
+ final VirtualDeviceId that = (VirtualDeviceId) obj;
+ return this.getClass() == that.getClass() &&
+ Objects.equal(this.networkId, that.networkId) &&
+ Objects.equal(this.deviceId, that.deviceId);
+ }
+ return false;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowObjectiveStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowObjectiveStore.java
new file mode 100644
index 0000000..5bcfabc
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowObjectiveStore.java
@@ -0,0 +1,78 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.Maps;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+
+import java.util.concurrent.ConcurrentMap;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Distributed flow objective store for virtual network.
+ */
+@Component(immediate = true, enabled = false, service = VirtualNetworkFlowObjectiveStore.class)
+public class DistributedVirtualFlowObjectiveStore
+ extends SimpleVirtualFlowObjectiveStore
+ implements VirtualNetworkFlowObjectiveStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private ConsistentMap<NetworkId, ConcurrentMap<Integer, byte[]>> nextGroupsMap;
+ private static final String VNET_FLOW_OBJ_GROUP_MAP_NAME =
+ "onos-networkId-flowobjective-groups";
+ private static final String VNET_FLOW_OBJ_GROUP_MAP_FRIENDLYNAME =
+ "DistributedVirtualFlowObjectiveStore";
+
+ @Override
+ protected void initNextGroupsMap() {
+ nextGroupsMap = storageService.<NetworkId, ConcurrentMap<Integer, byte[]>>consistentMapBuilder()
+ .withName(VNET_FLOW_OBJ_GROUP_MAP_NAME)
+ .withSerializer(Serializer.using(
+ new KryoNamespace.Builder()
+ .register(KryoNamespaces.API)
+ .register(NetworkId.class)
+ .build(VNET_FLOW_OBJ_GROUP_MAP_FRIENDLYNAME)))
+ .build();
+
+ }
+
+ @Override
+ protected ConcurrentMap<Integer, byte[]> getNextGroups(NetworkId networkId) {
+ nextGroupsMap.computeIfAbsent(networkId, n -> {
+ log.debug("getNextGroups - creating new ConcurrentMap");
+ return Maps.newConcurrentMap();
+ });
+
+ return nextGroupsMap.get(networkId).value();
+ }
+
+ @Override
+ protected void updateNextGroupsMap(NetworkId networkId, ConcurrentMap<Integer,
+ byte[]> nextGroups) {
+ nextGroupsMap.put(networkId, nextGroups);
+ }
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowRuleStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowRuleStore.java
new file mode 100644
index 0000000..8722c92
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualFlowRuleStore.java
@@ -0,0 +1,922 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.store.impl.primitives.VirtualDeviceId;
+import org.onosproject.incubator.net.virtual.store.impl.primitives.VirtualFlowEntry;
+import org.onosproject.incubator.net.virtual.store.impl.primitives.VirtualFlowRule;
+import org.onosproject.incubator.net.virtual.store.impl.primitives.VirtualFlowRuleBatchEvent;
+import org.onosproject.incubator.net.virtual.store.impl.primitives.VirtualFlowRuleBatchOperation;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.BatchOperationEntry;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchRequest;
+import org.onosproject.store.Timestamp;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.cluster.messaging.ClusterMessage;
+import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.incubator.net.virtual.store.impl.OsgiPropertyConstants.*;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages inventory of flow rules using a distributed state management protocol
+ * for virtual networks.
+ */
+//TODO: support backup and persistent mechanism
+@Component(immediate = true, enabled = false, service = VirtualNetworkFlowRuleStore.class,
+ property = {
+ MESSAGE_HANDLER_THREAD_POOL_SIZE + ":Integer=" + MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT,
+ BACKUP_PERIOD_MILLIS + ":Integer=" + BACKUP_PERIOD_MILLIS_DEFAULT,
+ PERSISTENCE_ENABLED + ":Boolean=" + PERSISTENCE_ENABLED_DEFAULT,
+ })
+
+public class DistributedVirtualFlowRuleStore
+ extends AbstractVirtualStore<FlowRuleBatchEvent, FlowRuleStoreDelegate>
+ implements VirtualNetworkFlowRuleStore {
+
+ private final Logger log = getLogger(getClass());
+
+ //TODO: confirm this working fine with multiple thread more than 1
+ private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 5000;
+
+ private static final String FLOW_OP_TOPIC = "virtual-flow-ops-ids";
+
+ // MessageSubjects used by DistributedVirtualFlowRuleStore peer-peer communication.
+ private static final MessageSubject APPLY_BATCH_FLOWS
+ = new MessageSubject("virtual-peer-forward-apply-batch");
+ private static final MessageSubject GET_FLOW_ENTRY
+ = new MessageSubject("virtual-peer-forward-get-flow-entry");
+ private static final MessageSubject GET_DEVICE_FLOW_ENTRIES
+ = new MessageSubject("virtual-peer-forward-get-device-flow-entries");
+ private static final MessageSubject REMOVE_FLOW_ENTRY
+ = new MessageSubject("virtual-peer-forward-remove-flow-entry");
+ private static final MessageSubject REMOTE_APPLY_COMPLETED
+ = new MessageSubject("virtual-peer-apply-completed");
+
+ /** Number of threads in the message handler pool. */
+ private int msgHandlerThreadPoolSize = MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT;
+
+ /** Delay in ms between successive backup runs. */
+ private int backupPeriod = BACKUP_PERIOD_MILLIS_DEFAULT;
+
+ /** Indicates whether or not changes in the flow table should be persisted to disk.. */
+ private boolean persistenceEnabled = PERSISTENCE_ENABLED_DEFAULT;
+
+ private InternalFlowTable flowTable = new InternalFlowTable();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ComponentConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VirtualNetworkService vnaService;
+
+ private Map<Long, NodeId> pendingResponses = Maps.newConcurrentMap();
+ private ExecutorService messageHandlingExecutor;
+ private ExecutorService eventHandler;
+
+ private EventuallyConsistentMap<NetworkId, Map<DeviceId, List<TableStatisticsEntry>>> deviceTableStats;
+ private final EventuallyConsistentMapListener<NetworkId, Map<DeviceId, List<TableStatisticsEntry>>>
+ tableStatsListener = new InternalTableStatsListener();
+
+
+ protected final Serializer serializer = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(NetworkId.class)
+ .register(VirtualFlowRuleBatchOperation.class)
+ .register(VirtualFlowRuleBatchEvent.class)
+ .build());
+
+ protected final KryoNamespace.Builder serializerBuilder = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(MastershipBasedTimestamp.class);
+
+ private IdGenerator idGenerator;
+ private NodeId local;
+
+
+ @Activate
+ public void activate(ComponentContext context) {
+ configService.registerProperties(getClass());
+
+ idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC);
+
+ local = clusterService.getLocalNode().id();
+
+ eventHandler = Executors.newSingleThreadExecutor(
+ groupedThreads("onos/virtual-flow", "event-handler", log));
+ messageHandlingExecutor = Executors.newFixedThreadPool(
+ msgHandlerThreadPoolSize, groupedThreads("onos/store/virtual-flow", "message-handlers", log));
+
+ registerMessageHandlers(messageHandlingExecutor);
+
+ deviceTableStats = storageService
+ .<NetworkId, Map<DeviceId, List<TableStatisticsEntry>>>eventuallyConsistentMapBuilder()
+ .withName("onos-virtual-flow-table-stats")
+ .withSerializer(serializerBuilder)
+ .withAntiEntropyPeriod(5, TimeUnit.SECONDS)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .withTombstonesDisabled()
+ .build();
+ deviceTableStats.addListener(tableStatsListener);
+
+ logConfig("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ configService.unregisterProperties(getClass(), false);
+ unregisterMessageHandlers();
+ deviceTableStats.removeListener(tableStatsListener);
+ deviceTableStats.destroy();
+ eventHandler.shutdownNow();
+ messageHandlingExecutor.shutdownNow();
+ log.info("Stopped");
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ logConfig("Default config");
+ return;
+ }
+
+ Dictionary properties = context.getProperties();
+ int newPoolSize;
+ int newBackupPeriod;
+ try {
+ String s = get(properties, MESSAGE_HANDLER_THREAD_POOL_SIZE);
+ newPoolSize = isNullOrEmpty(s) ? msgHandlerThreadPoolSize : Integer.parseInt(s.trim());
+
+ s = get(properties, BACKUP_PERIOD_MILLIS);
+ newBackupPeriod = isNullOrEmpty(s) ? backupPeriod : Integer.parseInt(s.trim());
+ } catch (NumberFormatException | ClassCastException e) {
+ newPoolSize = MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT;
+ newBackupPeriod = BACKUP_PERIOD_MILLIS_DEFAULT;
+ }
+
+ boolean restartBackupTask = false;
+
+ if (newBackupPeriod != backupPeriod) {
+ backupPeriod = newBackupPeriod;
+ restartBackupTask = true;
+ }
+ if (restartBackupTask) {
+ log.warn("Currently, backup tasks are not supported.");
+ }
+ if (newPoolSize != msgHandlerThreadPoolSize) {
+ msgHandlerThreadPoolSize = newPoolSize;
+ ExecutorService oldMsgHandler = messageHandlingExecutor;
+ messageHandlingExecutor = Executors.newFixedThreadPool(
+ msgHandlerThreadPoolSize, groupedThreads("onos/store/virtual-flow", "message-handlers", log));
+
+ // replace previously registered handlers.
+ registerMessageHandlers(messageHandlingExecutor);
+ oldMsgHandler.shutdown();
+ }
+
+ logConfig("Reconfigured");
+ }
+
+ @Override
+ public int getFlowRuleCount(NetworkId networkId) {
+ AtomicInteger sum = new AtomicInteger(0);
+ DeviceService deviceService = vnaService.get(networkId, DeviceService.class);
+ deviceService.getDevices()
+ .forEach(device -> sum.addAndGet(
+ Iterables.size(getFlowEntries(networkId, device.id()))));
+ return sum.get();
+ }
+
+ @Override
+ public FlowEntry getFlowEntry(NetworkId networkId, FlowRule rule) {
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(rule.deviceId());
+
+ if (master == null) {
+ log.debug("Failed to getFlowEntry: No master for {}, vnet {}",
+ rule.deviceId(), networkId);
+ return null;
+ }
+
+ if (Objects.equals(local, master)) {
+ return flowTable.getFlowEntry(networkId, rule);
+ }
+
+ log.trace("Forwarding getFlowEntry to {}, which is the primary (master) " +
+ "for device {}, vnet {}",
+ master, rule.deviceId(), networkId);
+
+ VirtualFlowRule vRule = new VirtualFlowRule(networkId, rule);
+
+ return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive(vRule,
+ GET_FLOW_ENTRY,
+ serializer::encode,
+ serializer::decode,
+ master),
+ FLOW_RULE_STORE_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS,
+ null);
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId) {
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(deviceId);
+
+ if (master == null) {
+ log.debug("Failed to getFlowEntries: No master for {}, vnet {}", deviceId, networkId);
+ return Collections.emptyList();
+ }
+
+ if (Objects.equals(local, master)) {
+ return flowTable.getFlowEntries(networkId, deviceId);
+ }
+
+ log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
+ master, deviceId);
+
+ return Tools.futureGetOrElse(
+ clusterCommunicator.sendAndReceive(deviceId,
+ GET_DEVICE_FLOW_ENTRIES,
+ serializer::encode,
+ serializer::decode,
+ master),
+ FLOW_RULE_STORE_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS,
+ Collections.emptyList());
+ }
+
+ @Override
+ public void storeBatch(NetworkId networkId, FlowRuleBatchOperation operation) {
+ if (operation.getOperations().isEmpty()) {
+ notifyDelegate(networkId, FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
+ new CompletedBatchOperation(true, Collections.emptySet(), operation.deviceId())));
+ return;
+ }
+
+ DeviceId deviceId = operation.deviceId();
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(deviceId);
+
+ if (master == null) {
+ log.warn("No master for {}, vnet {} : flows will be marked for removal", deviceId, networkId);
+
+ updateStoreInternal(networkId, operation);
+
+ notifyDelegate(networkId, FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
+ new CompletedBatchOperation(true, Collections.emptySet(), operation.deviceId())));
+ return;
+ }
+
+ if (Objects.equals(local, master)) {
+ storeBatchInternal(networkId, operation);
+ return;
+ }
+
+ log.trace("Forwarding storeBatch to {}, which is the primary (master) for device {}, vent {}",
+ master, deviceId, networkId);
+
+ clusterCommunicator.unicast(new VirtualFlowRuleBatchOperation(networkId, operation),
+ APPLY_BATCH_FLOWS,
+ serializer::encode,
+ master)
+ .whenComplete((result, error) -> {
+ if (error != null) {
+ log.warn("Failed to storeBatch: {} to {}", operation, master, error);
+
+ Set<FlowRule> allFailures = operation.getOperations()
+ .stream()
+ .map(BatchOperationEntry::target)
+ .collect(Collectors.toSet());
+
+ notifyDelegate(networkId, FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
+ new CompletedBatchOperation(false, allFailures, deviceId)));
+ }
+ });
+ }
+
+ @Override
+ public void batchOperationComplete(NetworkId networkId, FlowRuleBatchEvent event) {
+ //FIXME: need a per device pending response
+ NodeId nodeId = pendingResponses.remove(event.subject().batchId());
+ if (nodeId == null) {
+ notifyDelegate(networkId, event);
+ } else {
+ // TODO check unicast return value
+ clusterCommunicator.unicast(new VirtualFlowRuleBatchEvent(networkId, event),
+ REMOTE_APPLY_COMPLETED, serializer::encode, nodeId);
+ //error log: log.warn("Failed to respond to peer for batch operation result");
+ }
+ }
+
+ @Override
+ public void deleteFlowRule(NetworkId networkId, FlowRule rule) {
+ storeBatch(networkId,
+ new FlowRuleBatchOperation(
+ Collections.singletonList(
+ new FlowRuleBatchEntry(
+ FlowRuleBatchEntry.FlowRuleOperation.REMOVE,
+ rule)), rule.deviceId(), idGenerator.getNewId()));
+ }
+
+ @Override
+ public FlowRuleEvent addOrUpdateFlowRule(NetworkId networkId, FlowEntry rule) {
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(rule.deviceId());
+ if (Objects.equals(local, master)) {
+ return addOrUpdateFlowRuleInternal(networkId, rule);
+ }
+
+ log.warn("Tried to update FlowRule {} state,"
+ + " while the Node was not the master.", rule);
+ return null;
+ }
+
+ private FlowRuleEvent addOrUpdateFlowRuleInternal(NetworkId networkId, FlowEntry rule) {
+ // check if this new rule is an update to an existing entry
+ StoredFlowEntry stored = flowTable.getFlowEntry(networkId, rule);
+ if (stored != null) {
+ //FIXME modification of "stored" flow entry outside of flow table
+ stored.setBytes(rule.bytes());
+ stored.setLife(rule.life(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS);
+ stored.setLiveType(rule.liveType());
+ stored.setPackets(rule.packets());
+ stored.setLastSeen();
+ if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntry.FlowEntryState.ADDED);
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED, rule);
+ }
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_UPDATED, rule);
+ }
+
+ // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore
+ // TODO: also update backup if the behavior is correct.
+ flowTable.add(networkId, rule);
+ return null;
+ }
+
+ @Override
+ public FlowRuleEvent removeFlowRule(NetworkId networkId, FlowEntry rule) {
+ final DeviceId deviceId = rule.deviceId();
+
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(deviceId);
+
+ if (Objects.equals(local, master)) {
+ // bypass and handle it locally
+ return removeFlowRuleInternal(new VirtualFlowEntry(networkId, rule));
+ }
+
+ if (master == null) {
+ log.warn("Failed to removeFlowRule: No master for {}", deviceId);
+ // TODO: revisit if this should be null (="no-op") or Exception
+ return null;
+ }
+
+ log.trace("Forwarding removeFlowRule to {}, which is the master for device {}",
+ master, deviceId);
+
+ return Futures.getUnchecked(clusterCommunicator.sendAndReceive(
+ new VirtualFlowEntry(networkId, rule),
+ REMOVE_FLOW_ENTRY,
+ serializer::encode,
+ serializer::decode,
+ master));
+ }
+
+ @Override
+ public FlowRuleEvent pendingFlowRule(NetworkId networkId, FlowEntry rule) {
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ if (mastershipService.isLocalMaster(rule.deviceId())) {
+ StoredFlowEntry stored = flowTable.getFlowEntry(networkId, rule);
+ if (stored != null &&
+ stored.state() != FlowEntry.FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntry.FlowEntryState.PENDING_ADD);
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_UPDATED, rule);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void purgeFlowRules(NetworkId networkId) {
+ flowTable.purgeFlowRules(networkId);
+ }
+
+ @Override
+ public FlowRuleEvent updateTableStatistics(NetworkId networkId,
+ DeviceId deviceId,
+ List<TableStatisticsEntry> tableStats) {
+ if (deviceTableStats.get(networkId) == null) {
+ deviceTableStats.put(networkId, Maps.newConcurrentMap());
+ }
+ deviceTableStats.get(networkId).put(deviceId, tableStats);
+ return null;
+ }
+
+ @Override
+ public Iterable<TableStatisticsEntry> getTableStatistics(NetworkId networkId, DeviceId deviceId) {
+ MastershipService mastershipService =
+ vnaService.get(networkId, MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(deviceId);
+
+ if (master == null) {
+ log.debug("Failed to getTableStats: No master for {}", deviceId);
+ return Collections.emptyList();
+ }
+
+ if (deviceTableStats.get(networkId) == null) {
+ deviceTableStats.put(networkId, Maps.newConcurrentMap());
+ }
+
+ List<TableStatisticsEntry> tableStats = deviceTableStats.get(networkId).get(deviceId);
+ if (tableStats == null) {
+ return Collections.emptyList();
+ }
+ return ImmutableList.copyOf(tableStats);
+ }
+
+ private void registerMessageHandlers(ExecutorService executor) {
+ clusterCommunicator.addSubscriber(APPLY_BATCH_FLOWS, new OnStoreBatch(), executor);
+ clusterCommunicator.<VirtualFlowRuleBatchEvent>addSubscriber(
+ REMOTE_APPLY_COMPLETED, serializer::decode,
+ this::notifyDelicateByNetwork, executor);
+ clusterCommunicator.addSubscriber(
+ GET_FLOW_ENTRY, serializer::decode, this::getFlowEntryByNetwork,
+ serializer::encode, executor);
+ clusterCommunicator.addSubscriber(
+ GET_DEVICE_FLOW_ENTRIES, serializer::decode,
+ this::getFlowEntriesByNetwork,
+ serializer::encode, executor);
+ clusterCommunicator.addSubscriber(
+ REMOVE_FLOW_ENTRY, serializer::decode, this::removeFlowRuleInternal,
+ serializer::encode, executor);
+ }
+
+ private void unregisterMessageHandlers() {
+ clusterCommunicator.removeSubscriber(REMOVE_FLOW_ENTRY);
+ clusterCommunicator.removeSubscriber(GET_DEVICE_FLOW_ENTRIES);
+ clusterCommunicator.removeSubscriber(GET_FLOW_ENTRY);
+ clusterCommunicator.removeSubscriber(APPLY_BATCH_FLOWS);
+ clusterCommunicator.removeSubscriber(REMOTE_APPLY_COMPLETED);
+ }
+
+
+ private void logConfig(String prefix) {
+ log.info("{} with msgHandlerPoolSize = {}; backupPeriod = {}",
+ prefix, msgHandlerThreadPoolSize, backupPeriod);
+ }
+
+ private void storeBatchInternal(NetworkId networkId, FlowRuleBatchOperation operation) {
+
+ final DeviceId did = operation.deviceId();
+ //final Collection<FlowEntry> ft = flowTable.getFlowEntries(did);
+ Set<FlowRuleBatchEntry> currentOps = updateStoreInternal(networkId, operation);
+ if (currentOps.isEmpty()) {
+ batchOperationComplete(networkId, FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(operation.id(), Collections.emptySet()),
+ new CompletedBatchOperation(true, Collections.emptySet(), did)));
+ return;
+ }
+
+ //Confirm that flowrule service is created
+ vnaService.get(networkId, FlowRuleService.class);
+
+ notifyDelegate(networkId, FlowRuleBatchEvent.requested(new
+ FlowRuleBatchRequest(operation.id(),
+ currentOps), operation.deviceId()));
+ }
+
+ private Set<FlowRuleBatchEntry> updateStoreInternal(NetworkId networkId,
+ FlowRuleBatchOperation operation) {
+ return operation.getOperations().stream().map(
+ op -> {
+ StoredFlowEntry entry;
+ switch (op.operator()) {
+ case ADD:
+ entry = new DefaultFlowEntry(op.target());
+ // always add requested FlowRule
+ // Note: 2 equal FlowEntry may have different treatment
+ flowTable.remove(networkId, entry.deviceId(), entry);
+ flowTable.add(networkId, entry);
+
+ return op;
+ case REMOVE:
+ entry = flowTable.getFlowEntry(networkId, op.target());
+ if (entry != null) {
+ //FIXME modification of "stored" flow entry outside of flow table
+ entry.setState(FlowEntry.FlowEntryState.PENDING_REMOVE);
+ log.debug("Setting state of rule to pending remove: {}", entry);
+ return op;
+ }
+ break;
+ case MODIFY:
+ //TODO: figure this out at some point
+ break;
+ default:
+ log.warn("Unknown flow operation operator: {}", op.operator());
+ }
+ return null;
+ }
+ ).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ private FlowRuleEvent removeFlowRuleInternal(VirtualFlowEntry rule) {
+ final DeviceId deviceId = rule.flowEntry().deviceId();
+ // This is where one could mark a rule as removed and still keep it in the store.
+ final FlowEntry removed = flowTable.remove(rule.networkId(), deviceId, rule.flowEntry());
+ // rule may be partial rule that is missing treatment, we should use rule from store instead
+ return removed != null ? new FlowRuleEvent(RULE_REMOVED, removed) : null;
+ }
+
+ private final class OnStoreBatch implements ClusterMessageHandler {
+
+ @Override
+ public void handle(final ClusterMessage message) {
+ VirtualFlowRuleBatchOperation vOperation = serializer.decode(message.payload());
+ log.debug("received batch request {}", vOperation);
+
+ FlowRuleBatchOperation operation = vOperation.operation();
+
+ final DeviceId deviceId = operation.deviceId();
+ MastershipService mastershipService =
+ vnaService.get(vOperation.networkId(), MastershipService.class);
+ NodeId master = mastershipService.getMasterFor(deviceId);
+ if (!Objects.equals(local, master)) {
+ Set<FlowRule> failures = new HashSet<>(operation.size());
+ for (FlowRuleBatchEntry op : operation.getOperations()) {
+ failures.add(op.target());
+ }
+ CompletedBatchOperation allFailed = new CompletedBatchOperation(false, failures, deviceId);
+ // This node is no longer the master, respond as all failed.
+ // TODO: we might want to wrap response in envelope
+ // to distinguish sw programming failure and hand over
+ // it make sense in the latter case to retry immediately.
+ message.respond(serializer.encode(allFailed));
+ return;
+ }
+
+ pendingResponses.put(operation.id(), message.sender());
+ storeBatchInternal(vOperation.networkId(), operation);
+ }
+ }
+
+ /**
+ * Returns flow rule entry using virtual flow rule.
+ *
+ * @param rule an encapsulated flow rule to be queried
+ */
+ private FlowEntry getFlowEntryByNetwork(VirtualFlowRule rule) {
+ return flowTable.getFlowEntry(rule.networkId(), rule.rule());
+ }
+
+ /**
+ * returns flow entries using virtual device id.
+ *
+ * @param deviceId an encapsulated virtual device id
+ * @return a set of flow entries
+ */
+ private Set<FlowEntry> getFlowEntriesByNetwork(VirtualDeviceId deviceId) {
+ return flowTable.getFlowEntries(deviceId.networkId(), deviceId.deviceId());
+ }
+
+ /**
+ * span out Flow Rule Batch event according to virtual network id.
+ *
+ * @param event a event to be span out
+ */
+ private void notifyDelicateByNetwork(VirtualFlowRuleBatchEvent event) {
+ batchOperationComplete(event.networkId(), event.event());
+ }
+
+ private class InternalFlowTable {
+ //TODO replace the Map<V,V> with ExtendedSet
+ //TODO: support backup mechanism
+ private final Map<NetworkId, Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>>
+ flowEntriesMap = Maps.newConcurrentMap();
+ private final Map<NetworkId, Map<DeviceId, Long>> lastUpdateTimesMap = Maps.newConcurrentMap();
+
+ private Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
+ getFlowEntriesByNetwork(NetworkId networkId) {
+ return flowEntriesMap.computeIfAbsent(networkId, k -> Maps.newConcurrentMap());
+ }
+
+ private Map<DeviceId, Long> getLastUpdateTimesByNetwork(NetworkId networkId) {
+ return lastUpdateTimesMap.computeIfAbsent(networkId, k -> Maps.newConcurrentMap());
+ }
+
+ /**
+ * Returns the flow table for specified device.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId identifier of the device
+ * @return Map representing Flow Table of given device.
+ */
+ private Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>
+ getFlowTable(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
+ flowEntries = getFlowEntriesByNetwork(networkId);
+ if (persistenceEnabled) {
+ //TODO: support persistent
+ log.warn("Persistent is not supported");
+ return null;
+ } else {
+ return flowEntries.computeIfAbsent(deviceId, id -> Maps.newConcurrentMap());
+ }
+ }
+
+ private Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>
+ getFlowTableCopy(NetworkId networkId, DeviceId deviceId) {
+
+ Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
+ flowEntries = getFlowEntriesByNetwork(networkId);
+ Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>> copy = Maps.newHashMap();
+
+ if (persistenceEnabled) {
+ //TODO: support persistent
+ log.warn("Persistent is not supported");
+ return null;
+ } else {
+ flowEntries.computeIfAbsent(deviceId, id -> Maps.newConcurrentMap()).forEach((k, v) -> {
+ copy.put(k, Maps.newHashMap(v));
+ });
+ return copy;
+ }
+ }
+
+ private Map<StoredFlowEntry, StoredFlowEntry>
+ getFlowEntriesInternal(NetworkId networkId, DeviceId deviceId, FlowId flowId) {
+
+ return getFlowTable(networkId, deviceId)
+ .computeIfAbsent(flowId, id -> Maps.newConcurrentMap());
+ }
+
+ private StoredFlowEntry getFlowEntryInternal(NetworkId networkId, FlowRule rule) {
+
+ return getFlowEntriesInternal(networkId, rule.deviceId(), rule.id()).get(rule);
+ }
+
+ private Set<FlowEntry> getFlowEntriesInternal(NetworkId networkId, DeviceId deviceId) {
+
+ return getFlowTable(networkId, deviceId).values().stream()
+ .flatMap(m -> m.values().stream())
+ .collect(Collectors.toSet());
+ }
+
+ public StoredFlowEntry getFlowEntry(NetworkId networkId, FlowRule rule) {
+ return getFlowEntryInternal(networkId, rule);
+ }
+
+ public Set<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId) {
+
+ return getFlowEntriesInternal(networkId, deviceId);
+ }
+
+ public void add(NetworkId networkId, FlowEntry rule) {
+ Map<DeviceId, Long> lastUpdateTimes = getLastUpdateTimesByNetwork(networkId);
+
+ getFlowEntriesInternal(networkId, rule.deviceId(), rule.id())
+ .compute((StoredFlowEntry) rule, (k, stored) -> {
+ //TODO compare stored and rule timestamps
+ //TODO the key is not updated
+ return (StoredFlowEntry) rule;
+ });
+ lastUpdateTimes.put(rule.deviceId(), System.currentTimeMillis());
+ }
+
+ public FlowEntry remove(NetworkId networkId, DeviceId deviceId, FlowEntry rule) {
+ final AtomicReference<FlowEntry> removedRule = new AtomicReference<>();
+ Map<DeviceId, Long> lastUpdateTimes = getLastUpdateTimesByNetwork(networkId);
+
+ getFlowEntriesInternal(networkId, rule.deviceId(), rule.id())
+ .computeIfPresent((StoredFlowEntry) rule, (k, stored) -> {
+ if (rule instanceof DefaultFlowEntry) {
+ DefaultFlowEntry toRemove = (DefaultFlowEntry) rule;
+ if (stored instanceof DefaultFlowEntry) {
+ DefaultFlowEntry storedEntry = (DefaultFlowEntry) stored;
+ if (toRemove.created() < storedEntry.created()) {
+ log.debug("Trying to remove more recent flow entry {} (stored: {})",
+ toRemove, stored);
+ // the key is not updated, removedRule remains null
+ return stored;
+ }
+ }
+ }
+ removedRule.set(stored);
+ return null;
+ });
+
+ if (removedRule.get() != null) {
+ lastUpdateTimes.put(deviceId, System.currentTimeMillis());
+ return removedRule.get();
+ } else {
+ return null;
+ }
+ }
+
+ public void purgeFlowRule(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
+ flowEntries = getFlowEntriesByNetwork(networkId);
+ flowEntries.remove(deviceId);
+ }
+
+ public void purgeFlowRules(NetworkId networkId) {
+ Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
+ flowEntries = getFlowEntriesByNetwork(networkId);
+ flowEntries.clear();
+ }
+ }
+
+ private class InternalTableStatsListener
+ implements EventuallyConsistentMapListener<NetworkId, Map<DeviceId, List<TableStatisticsEntry>>> {
+
+ @Override
+ public void event(EventuallyConsistentMapEvent<NetworkId, Map<DeviceId,
+ List<TableStatisticsEntry>>> event) {
+ //TODO: Generate an event to listeners (do we need?)
+ }
+ }
+
+ public final class MastershipBasedTimestamp implements Timestamp {
+
+ private final long termNumber;
+ private final long sequenceNumber;
+
+ /**
+ * Default constructor for serialization.
+ */
+ protected MastershipBasedTimestamp() {
+ this.termNumber = -1;
+ this.sequenceNumber = -1;
+ }
+
+ /**
+ * Default version tuple.
+ *
+ * @param termNumber the mastership termNumber
+ * @param sequenceNumber the sequenceNumber number within the termNumber
+ */
+ public MastershipBasedTimestamp(long termNumber, long sequenceNumber) {
+ this.termNumber = termNumber;
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ @Override
+ public int compareTo(Timestamp o) {
+ checkArgument(o instanceof MastershipBasedTimestamp,
+ "Must be MastershipBasedTimestamp", o);
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) o;
+
+ return ComparisonChain.start()
+ .compare(this.termNumber, that.termNumber)
+ .compare(this.sequenceNumber, that.sequenceNumber)
+ .result();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(termNumber, sequenceNumber);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof MastershipBasedTimestamp)) {
+ return false;
+ }
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) obj;
+ return Objects.equals(this.termNumber, that.termNumber) &&
+ Objects.equals(this.sequenceNumber, that.sequenceNumber);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("termNumber", termNumber)
+ .add("sequenceNumber", sequenceNumber)
+ .toString();
+ }
+
+ /**
+ * Returns the termNumber.
+ *
+ * @return termNumber
+ */
+ public long termNumber() {
+ return termNumber;
+ }
+
+ /**
+ * Returns the sequenceNumber number.
+ *
+ * @return sequenceNumber
+ */
+ public long sequenceNumber() {
+ return sequenceNumber;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualNetworkStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualNetworkStore.java
new file mode 100644
index 0000000..b756c62
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualNetworkStore.java
@@ -0,0 +1,954 @@
+/*
+ * Copyright 2015-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.store.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualHost;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.Key;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.SetEvent;
+import org.onosproject.store.service.SetEventListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiFunction;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the virtual network store.
+ */
+@Component(immediate = true, service = VirtualNetworkStore.class)
+public class DistributedVirtualNetworkStore
+ extends AbstractStore<VirtualNetworkEvent, VirtualNetworkStoreDelegate>
+ implements VirtualNetworkStore {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ private IdGenerator idGenerator;
+
+ // Track tenants by ID
+ private DistributedSet<TenantId> tenantIdSet;
+
+ // Listener for tenant events
+ private final SetEventListener<TenantId> setListener = new InternalSetListener();
+
+ // Track virtual networks by network Id
+ private ConsistentMap<NetworkId, VirtualNetwork> networkIdVirtualNetworkConsistentMap;
+ private Map<NetworkId, VirtualNetwork> networkIdVirtualNetworkMap;
+
+ // Listener for virtual network events
+ private final MapEventListener<NetworkId, VirtualNetwork> virtualNetworkMapListener =
+ new InternalMapListener<>((mapEventType, virtualNetwork) -> {
+ VirtualNetworkEvent.Type eventType =
+ mapEventType.equals(MapEvent.Type.INSERT)
+ ? VirtualNetworkEvent.Type.NETWORK_ADDED :
+ mapEventType.equals(MapEvent.Type.UPDATE)
+ ? VirtualNetworkEvent.Type.NETWORK_UPDATED :
+ mapEventType.equals(MapEvent.Type.REMOVE)
+ ? VirtualNetworkEvent.Type.NETWORK_REMOVED : null;
+ return eventType == null ? null : new VirtualNetworkEvent(eventType, virtualNetwork.id());
+ });
+
+ // Listener for virtual device events
+ private final MapEventListener<VirtualDeviceId, VirtualDevice> virtualDeviceMapListener =
+ new InternalMapListener<>((mapEventType, virtualDevice) -> {
+ VirtualNetworkEvent.Type eventType =
+ mapEventType.equals(MapEvent.Type.INSERT)
+ ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED :
+ mapEventType.equals(MapEvent.Type.UPDATE)
+ ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_UPDATED :
+ mapEventType.equals(MapEvent.Type.REMOVE)
+ ? VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED : null;
+ return eventType == null ? null :
+ new VirtualNetworkEvent(eventType, virtualDevice.networkId(), virtualDevice);
+ });
+
+ // Track virtual network IDs by tenant Id
+ private ConsistentMap<TenantId, Set<NetworkId>> tenantIdNetworkIdSetConsistentMap;
+ private Map<TenantId, Set<NetworkId>> tenantIdNetworkIdSetMap;
+
+ // Track virtual devices by device Id
+ private ConsistentMap<VirtualDeviceId, VirtualDevice> deviceIdVirtualDeviceConsistentMap;
+ private Map<VirtualDeviceId, VirtualDevice> deviceIdVirtualDeviceMap;
+
+ // Track device IDs by network Id
+ private ConsistentMap<NetworkId, Set<DeviceId>> networkIdDeviceIdSetConsistentMap;
+ private Map<NetworkId, Set<DeviceId>> networkIdDeviceIdSetMap;
+
+ // Track virtual hosts by host Id
+ private ConsistentMap<HostId, VirtualHost> hostIdVirtualHostConsistentMap;
+ private Map<HostId, VirtualHost> hostIdVirtualHostMap;
+
+ // Track host IDs by network Id
+ private ConsistentMap<NetworkId, Set<HostId>> networkIdHostIdSetConsistentMap;
+ private Map<NetworkId, Set<HostId>> networkIdHostIdSetMap;
+
+ // Track virtual links by network Id
+ private ConsistentMap<NetworkId, Set<VirtualLink>> networkIdVirtualLinkSetConsistentMap;
+ private Map<NetworkId, Set<VirtualLink>> networkIdVirtualLinkSetMap;
+
+ // Track virtual ports by network Id
+ private ConsistentMap<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetConsistentMap;
+ private Map<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetMap;
+
+ // Track intent ID to TunnelIds
+ private ConsistentMap<Key, Set<TunnelId>> intentKeyTunnelIdSetConsistentMap;
+ private Map<Key, Set<TunnelId>> intentKeyTunnelIdSetMap;
+
+ private static final Serializer SERIALIZER = Serializer
+ .using(new KryoNamespace.Builder().register(KryoNamespaces.API)
+ .register(TenantId.class)
+ .register(NetworkId.class)
+ .register(VirtualNetwork.class)
+ .register(DefaultVirtualNetwork.class)
+ .register(VirtualDevice.class)
+ .register(VirtualDeviceId.class)
+ .register(DefaultVirtualDevice.class)
+ .register(VirtualHost.class)
+ .register(DefaultVirtualHost.class)
+ .register(VirtualLink.class)
+ .register(DefaultVirtualLink.class)
+ .register(VirtualPort.class)
+ .register(DefaultVirtualPort.class)
+ .register(Device.class)
+ .register(TunnelId.class)
+ .register(VirtualNetworkIntent.class)
+ .register(WallClockTimestamp.class)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .build());
+
+ /**
+ * Distributed network store service activate method.
+ */
+ @Activate
+ public void activate() {
+ idGenerator = coreService.getIdGenerator(VirtualNetworkService.VIRTUAL_NETWORK_TOPIC);
+
+ tenantIdSet = storageService.<TenantId>setBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-tenantId")
+ .withRelaxedReadConsistency()
+ .build()
+ .asDistributedSet();
+ tenantIdSet.addListener(setListener);
+
+ networkIdVirtualNetworkConsistentMap = storageService.<NetworkId, VirtualNetwork>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-networkId-virtualnetwork")
+ .withRelaxedReadConsistency()
+ .build();
+ networkIdVirtualNetworkConsistentMap.addListener(virtualNetworkMapListener);
+ networkIdVirtualNetworkMap = networkIdVirtualNetworkConsistentMap.asJavaMap();
+
+ tenantIdNetworkIdSetConsistentMap = storageService.<TenantId, Set<NetworkId>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-tenantId-networkIds")
+ .withRelaxedReadConsistency()
+ .build();
+ tenantIdNetworkIdSetMap = tenantIdNetworkIdSetConsistentMap.asJavaMap();
+
+ deviceIdVirtualDeviceConsistentMap = storageService.<VirtualDeviceId, VirtualDevice>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-deviceId-virtualdevice")
+ .withRelaxedReadConsistency()
+ .build();
+ deviceIdVirtualDeviceConsistentMap.addListener(virtualDeviceMapListener);
+ deviceIdVirtualDeviceMap = deviceIdVirtualDeviceConsistentMap.asJavaMap();
+
+ networkIdDeviceIdSetConsistentMap = storageService.<NetworkId, Set<DeviceId>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-networkId-deviceIds")
+ .withRelaxedReadConsistency()
+ .build();
+ networkIdDeviceIdSetMap = networkIdDeviceIdSetConsistentMap.asJavaMap();
+
+ hostIdVirtualHostConsistentMap = storageService.<HostId, VirtualHost>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-hostId-virtualhost")
+ .withRelaxedReadConsistency()
+ .build();
+ hostIdVirtualHostMap = hostIdVirtualHostConsistentMap.asJavaMap();
+
+ networkIdHostIdSetConsistentMap = storageService.<NetworkId, Set<HostId>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-networkId-hostIds")
+ .withRelaxedReadConsistency()
+ .build();
+ networkIdHostIdSetMap = networkIdHostIdSetConsistentMap.asJavaMap();
+
+ networkIdVirtualLinkSetConsistentMap = storageService.<NetworkId, Set<VirtualLink>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-networkId-virtuallinks")
+ .withRelaxedReadConsistency()
+ .build();
+ networkIdVirtualLinkSetMap = networkIdVirtualLinkSetConsistentMap.asJavaMap();
+
+ networkIdVirtualPortSetConsistentMap = storageService.<NetworkId, Set<VirtualPort>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-networkId-virtualports")
+ .withRelaxedReadConsistency()
+ .build();
+ networkIdVirtualPortSetMap = networkIdVirtualPortSetConsistentMap.asJavaMap();
+
+ intentKeyTunnelIdSetConsistentMap = storageService.<Key, Set<TunnelId>>consistentMapBuilder()
+ .withSerializer(SERIALIZER)
+ .withName("onos-intentKey-tunnelIds")
+ .withRelaxedReadConsistency()
+ .build();
+ intentKeyTunnelIdSetMap = intentKeyTunnelIdSetConsistentMap.asJavaMap();
+
+ log.info("Started");
+ }
+
+ /**
+ * Distributed network store service deactivate method.
+ */
+ @Deactivate
+ public void deactivate() {
+ tenantIdSet.removeListener(setListener);
+ networkIdVirtualNetworkConsistentMap.removeListener(virtualNetworkMapListener);
+ deviceIdVirtualDeviceConsistentMap.removeListener(virtualDeviceMapListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addTenantId(TenantId tenantId) {
+ tenantIdSet.add(tenantId);
+ }
+
+ @Override
+ public void removeTenantId(TenantId tenantId) {
+ //Remove all the virtual networks of this tenant
+ Set<VirtualNetwork> networkIdSet = getNetworks(tenantId);
+ if (networkIdSet != null) {
+ networkIdSet.forEach(virtualNetwork -> removeNetwork(virtualNetwork.id()));
+ }
+
+ tenantIdSet.remove(tenantId);
+ }
+
+ @Override
+ public Set<TenantId> getTenantIds() {
+ return ImmutableSet.copyOf(tenantIdSet);
+ }
+
+ @Override
+ public VirtualNetwork addNetwork(TenantId tenantId) {
+
+ checkState(tenantIdSet.contains(tenantId), "The tenant has not been registered. " + tenantId.id());
+ VirtualNetwork virtualNetwork = new DefaultVirtualNetwork(genNetworkId(), tenantId);
+ //TODO update both maps in one transaction.
+ networkIdVirtualNetworkMap.put(virtualNetwork.id(), virtualNetwork);
+
+ Set<NetworkId> networkIdSet = tenantIdNetworkIdSetMap.get(tenantId);
+ if (networkIdSet == null) {
+ networkIdSet = new HashSet<>();
+ }
+ networkIdSet.add(virtualNetwork.id());
+ tenantIdNetworkIdSetMap.put(tenantId, networkIdSet);
+
+ return virtualNetwork;
+ }
+
+ /**
+ * Returns a new network identifier from a virtual network block of identifiers.
+ *
+ * @return NetworkId network identifier
+ */
+ private NetworkId genNetworkId() {
+ NetworkId networkId;
+ do {
+ networkId = NetworkId.networkId(idGenerator.getNewId());
+ } while (!networkId.isVirtualNetworkId());
+
+ return networkId;
+ }
+
+ @Override
+ public void removeNetwork(NetworkId networkId) {
+ // Make sure that the virtual network exists before attempting to remove it.
+ checkState(networkExists(networkId), "The network does not exist.");
+
+ //Remove all the devices of this network
+ Set<VirtualDevice> deviceSet = getDevices(networkId);
+ if (deviceSet != null) {
+ deviceSet.forEach(virtualDevice -> removeDevice(networkId, virtualDevice.id()));
+ }
+ //TODO update both maps in one transaction.
+
+ VirtualNetwork virtualNetwork = networkIdVirtualNetworkMap.remove(networkId);
+ if (virtualNetwork == null) {
+ return;
+ }
+ TenantId tenantId = virtualNetwork.tenantId();
+
+ Set<NetworkId> networkIdSet = new HashSet<>();
+ tenantIdNetworkIdSetMap.get(tenantId).forEach(networkId1 -> {
+ if (networkId1.id().equals(networkId.id())) {
+ networkIdSet.add(networkId1);
+ }
+ });
+
+ tenantIdNetworkIdSetMap.compute(virtualNetwork.tenantId(), (id, existingNetworkIds) -> {
+ if (existingNetworkIds == null || existingNetworkIds.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ return new HashSet<>(Sets.difference(existingNetworkIds, networkIdSet));
+ }
+ });
+ }
+
+ /**
+ * Returns if the network identifier exists.
+ *
+ * @param networkId network identifier
+ * @return true if the network identifier exists, false otherwise.
+ */
+ private boolean networkExists(NetworkId networkId) {
+ checkNotNull(networkId, "The network identifier cannot be null.");
+ return (networkIdVirtualNetworkMap.containsKey(networkId));
+ }
+
+ @Override
+ public VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+
+ Set<DeviceId> deviceIdSet = networkIdDeviceIdSetMap.get(networkId);
+ if (deviceIdSet == null) {
+ deviceIdSet = new HashSet<>();
+ }
+
+ checkState(!deviceIdSet.contains(deviceId), "The device already exists.");
+
+ VirtualDevice virtualDevice = new DefaultVirtualDevice(networkId, deviceId);
+ //TODO update both maps in one transaction.
+ deviceIdVirtualDeviceMap.put(new VirtualDeviceId(networkId, deviceId), virtualDevice);
+ deviceIdSet.add(deviceId);
+ networkIdDeviceIdSetMap.put(networkId, deviceIdSet);
+ return virtualDevice;
+ }
+
+ @Override
+ public void removeDevice(NetworkId networkId, DeviceId deviceId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ //Remove all the virtual ports of the this device
+ Set<VirtualPort> virtualPorts = getPorts(networkId, deviceId);
+ if (virtualPorts != null) {
+ virtualPorts.forEach(virtualPort -> removePort(networkId, deviceId, virtualPort.number()));
+ }
+ //TODO update both maps in one transaction.
+
+ Set<DeviceId> deviceIdSet = new HashSet<>();
+ networkIdDeviceIdSetMap.get(networkId).forEach(deviceId1 -> {
+ if (deviceId1.equals(deviceId)) {
+ deviceIdSet.add(deviceId1);
+ }
+ });
+
+ if (!deviceIdSet.isEmpty()) {
+ networkIdDeviceIdSetMap.compute(networkId, (id, existingDeviceIds) -> {
+ if (existingDeviceIds == null || existingDeviceIds.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ return new HashSet<>(Sets.difference(existingDeviceIds, deviceIdSet));
+ }
+ });
+
+ deviceIdVirtualDeviceMap.remove(new VirtualDeviceId(networkId, deviceId));
+ }
+ }
+
+ @Override
+ public VirtualHost addHost(NetworkId networkId, HostId hostId, MacAddress mac,
+ VlanId vlan, HostLocation location, Set<IpAddress> ips) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ checkState(virtualPortExists(networkId, location.deviceId(), location.port()),
+ "The virtual port has not been created.");
+ Set<HostId> hostIdSet = networkIdHostIdSetMap.get(networkId);
+ if (hostIdSet == null) {
+ hostIdSet = new HashSet<>();
+ }
+ VirtualHost virtualhost = new DefaultVirtualHost(networkId, hostId, mac, vlan, location, ips);
+ //TODO update both maps in one transaction.
+ hostIdVirtualHostMap.put(hostId, virtualhost);
+ hostIdSet.add(hostId);
+ networkIdHostIdSetMap.put(networkId, hostIdSet);
+ return virtualhost;
+ }
+
+ @Override
+ public void removeHost(NetworkId networkId, HostId hostId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ //TODO update both maps in one transaction.
+
+ Set<HostId> hostIdSet = new HashSet<>();
+ networkIdHostIdSetMap.get(networkId).forEach(hostId1 -> {
+ if (hostId1.equals(hostId)) {
+ hostIdSet.add(hostId1);
+ }
+ });
+
+ networkIdHostIdSetMap.compute(networkId, (id, existingHostIds) -> {
+ if (existingHostIds == null || existingHostIds.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ return new HashSet<>(Sets.difference(existingHostIds, hostIdSet));
+ }
+ });
+
+ hostIdVirtualHostMap.remove(hostId);
+ }
+
+ /**
+ * Returns if the given virtual port exists.
+ *
+ * @param networkId network identifier
+ * @param deviceId virtual device Id
+ * @param portNumber virtual port number
+ * @return true if the virtual port exists, false otherwise.
+ */
+ private boolean virtualPortExists(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+ Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+ if (virtualPortSet != null) {
+ return virtualPortSet.stream().anyMatch(
+ p -> p.element().id().equals(deviceId) &&
+ p.number().equals(portNumber));
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst,
+ Link.State state, TunnelId realizedBy) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ checkState(virtualPortExists(networkId, src.deviceId(), src.port()),
+ "The source virtual port has not been added.");
+ checkState(virtualPortExists(networkId, dst.deviceId(), dst.port()),
+ "The destination virtual port has not been added.");
+ Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
+ if (virtualLinkSet == null) {
+ virtualLinkSet = new HashSet<>();
+ }
+
+ // validate that the link does not already exist in this network
+ checkState(getLink(networkId, src, dst) == null,
+ "The virtual link already exists");
+ checkState(getLink(networkId, src, null) == null,
+ "The source connection point has been used by another link");
+ checkState(getLink(networkId, null, dst) == null,
+ "The destination connection point has been used by another link");
+
+ VirtualLink virtualLink = DefaultVirtualLink.builder()
+ .networkId(networkId)
+ .src(src)
+ .dst(dst)
+ .state(state)
+ .tunnelId(realizedBy)
+ .build();
+
+ virtualLinkSet.add(virtualLink);
+ networkIdVirtualLinkSetMap.put(networkId, virtualLinkSet);
+ return virtualLink;
+ }
+
+ @Override
+ public void updateLink(VirtualLink virtualLink, TunnelId tunnelId, Link.State state) {
+ checkState(networkExists(virtualLink.networkId()), "The network has not been added.");
+ Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(virtualLink.networkId());
+ if (virtualLinkSet == null) {
+ virtualLinkSet = new HashSet<>();
+ networkIdVirtualLinkSetMap.put(virtualLink.networkId(), virtualLinkSet);
+ log.warn("The updated virtual link {} has not been added", virtualLink);
+ return;
+ }
+ if (!virtualLinkSet.remove(virtualLink)) {
+ log.warn("The updated virtual link {} does not exist", virtualLink);
+ return;
+ }
+
+ VirtualLink newVirtualLink = DefaultVirtualLink.builder()
+ .networkId(virtualLink.networkId())
+ .src(virtualLink.src())
+ .dst(virtualLink.dst())
+ .tunnelId(tunnelId)
+ .state(state)
+ .build();
+
+ virtualLinkSet.add(newVirtualLink);
+ networkIdVirtualLinkSetMap.put(newVirtualLink.networkId(), virtualLinkSet);
+ }
+
+ @Override
+ public VirtualLink removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+ checkState(networkExists(networkId), "The network has not been added.");
+
+ final VirtualLink virtualLink = getLink(networkId, src, dst);
+ if (virtualLink == null) {
+ log.warn("The removed virtual link between {} and {} does not exist", src, dst);
+ return null;
+ }
+ Set<VirtualLink> virtualLinkSet = new HashSet<>();
+ virtualLinkSet.add(virtualLink);
+
+ networkIdVirtualLinkSetMap.compute(networkId, (id, existingVirtualLinks) -> {
+ if (existingVirtualLinks == null || existingVirtualLinks.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ return new HashSet<>(Sets.difference(existingVirtualLinks, virtualLinkSet));
+ }
+ });
+ return virtualLink;
+ }
+
+ @Override
+ public VirtualPort addPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+
+ if (virtualPortSet == null) {
+ virtualPortSet = new HashSet<>();
+ }
+
+ VirtualDevice device = deviceIdVirtualDeviceMap.get(new VirtualDeviceId(networkId, deviceId));
+ checkNotNull(device, "The device has not been created for deviceId: " + deviceId);
+
+ checkState(!virtualPortExists(networkId, deviceId, portNumber),
+ "The requested Port Number has been added.");
+
+ VirtualPort virtualPort = new DefaultVirtualPort(networkId, device,
+ portNumber, realizedBy);
+ virtualPortSet.add(virtualPort);
+ networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+ notifyDelegate(new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED,
+ networkId, device, virtualPort));
+ return virtualPort;
+ }
+
+ @Override
+ public void bindPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+
+ Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap
+ .get(networkId);
+
+ Optional<VirtualPort> virtualPortOptional = virtualPortSet.stream().filter(
+ p -> p.element().id().equals(deviceId) &&
+ p.number().equals(portNumber)).findFirst();
+ checkState(virtualPortOptional.isPresent(), "The virtual port has not been added.");
+
+ VirtualDevice device = deviceIdVirtualDeviceMap.get(new VirtualDeviceId(networkId, deviceId));
+ checkNotNull(device, "The device has not been created for deviceId: "
+ + deviceId);
+
+ VirtualPort vPort = virtualPortOptional.get();
+ virtualPortSet.remove(vPort);
+ vPort = new DefaultVirtualPort(networkId, device, portNumber, realizedBy);
+ virtualPortSet.add(vPort);
+ networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+ notifyDelegate(new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED,
+ networkId, device, vPort));
+ }
+
+ @Override
+ public void updatePortState(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, boolean isEnabled) {
+ checkState(networkExists(networkId), "No network with NetworkId %s exists.", networkId);
+
+ VirtualDevice device = deviceIdVirtualDeviceMap.get(new VirtualDeviceId(networkId, deviceId));
+ checkNotNull(device, "No device %s exists in NetworkId: %s", deviceId, networkId);
+
+ Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+ checkNotNull(virtualPortSet, "No port has been created for NetworkId: %s", networkId);
+
+ Optional<VirtualPort> virtualPortOptional = virtualPortSet.stream().filter(
+ p -> p.element().id().equals(deviceId) &&
+ p.number().equals(portNumber)).findFirst();
+ checkState(virtualPortOptional.isPresent(), "The virtual port has not been added.");
+
+ VirtualPort oldPort = virtualPortOptional.get();
+ if (oldPort.isEnabled() == isEnabled) {
+ log.debug("No change in port state - port not updated");
+ return;
+ }
+ VirtualPort newPort = new DefaultVirtualPort(networkId, device, portNumber, isEnabled,
+ oldPort.realizedBy());
+ virtualPortSet.remove(oldPort);
+ virtualPortSet.add(newPort);
+ networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+ notifyDelegate(new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED,
+ networkId, device, newPort));
+ log.debug("port state changed from {} to {}", oldPort.isEnabled(), isEnabled);
+ }
+
+ @Override
+ public void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ VirtualDevice device = deviceIdVirtualDeviceMap.get(new VirtualDeviceId(networkId, deviceId));
+ checkNotNull(device, "The device has not been created for deviceId: "
+ + deviceId);
+
+ if (networkIdVirtualPortSetMap.get(networkId) == null) {
+ log.warn("No port has been created for NetworkId: {}", networkId);
+ return;
+ }
+
+ Set<VirtualPort> virtualPortSet = new HashSet<>();
+ networkIdVirtualPortSetMap.get(networkId).forEach(port -> {
+ if (port.element().id().equals(deviceId) && port.number().equals(portNumber)) {
+ virtualPortSet.add(port);
+ }
+ });
+
+ if (!virtualPortSet.isEmpty()) {
+ AtomicBoolean portRemoved = new AtomicBoolean(false);
+ networkIdVirtualPortSetMap.compute(networkId, (id, existingVirtualPorts) -> {
+ if (existingVirtualPorts == null || existingVirtualPorts.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ portRemoved.set(true);
+ return new HashSet<>(Sets.difference(existingVirtualPorts, virtualPortSet));
+ }
+ });
+ if (portRemoved.get()) {
+ virtualPortSet.forEach(virtualPort -> notifyDelegate(
+ new VirtualNetworkEvent(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED,
+ networkId, device, virtualPort)
+ ));
+
+ //Remove all the virtual links connected to this virtual port
+ Set<VirtualLink> existingVirtualLinks = networkIdVirtualLinkSetMap.get(networkId);
+ if (existingVirtualLinks != null && !existingVirtualLinks.isEmpty()) {
+ Set<VirtualLink> virtualLinkSet = new HashSet<>();
+ ConnectPoint cp = new ConnectPoint(deviceId, portNumber);
+ existingVirtualLinks.forEach(virtualLink -> {
+ if (virtualLink.src().equals(cp) || virtualLink.dst().equals(cp)) {
+ virtualLinkSet.add(virtualLink);
+ }
+ });
+ virtualLinkSet.forEach(virtualLink ->
+ removeLink(networkId, virtualLink.src(), virtualLink.dst()));
+ }
+
+ //Remove all the hosts connected to this virtual port
+ Set<HostId> hostIdSet = new HashSet<>();
+ hostIdVirtualHostMap.forEach((hostId, virtualHost) -> {
+ if (virtualHost.location().deviceId().equals(deviceId) &&
+ virtualHost.location().port().equals(portNumber)) {
+ hostIdSet.add(hostId);
+ }
+ });
+ hostIdSet.forEach(hostId -> removeHost(networkId, hostId));
+ }
+ }
+ }
+
+ @Override
+ public Set<VirtualNetwork> getNetworks(TenantId tenantId) {
+ Set<NetworkId> networkIdSet = tenantIdNetworkIdSetMap.get(tenantId);
+ Set<VirtualNetwork> virtualNetworkSet = new HashSet<>();
+ if (networkIdSet != null) {
+ networkIdSet.forEach(networkId -> {
+ if (networkIdVirtualNetworkMap.get(networkId) != null) {
+ virtualNetworkSet.add(networkIdVirtualNetworkMap.get(networkId));
+ }
+ });
+ }
+ return ImmutableSet.copyOf(virtualNetworkSet);
+ }
+
+ @Override
+ public VirtualNetwork getNetwork(NetworkId networkId) {
+ return networkIdVirtualNetworkMap.get(networkId);
+ }
+
+ @Override
+ public Set<VirtualDevice> getDevices(NetworkId networkId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ Set<DeviceId> deviceIdSet = networkIdDeviceIdSetMap.get(networkId);
+ Set<VirtualDevice> virtualDeviceSet = new HashSet<>();
+ if (deviceIdSet != null) {
+ deviceIdSet.forEach(deviceId -> virtualDeviceSet.add(
+ deviceIdVirtualDeviceMap.get(new VirtualDeviceId(networkId, deviceId))));
+ }
+ return ImmutableSet.copyOf(virtualDeviceSet);
+ }
+
+ @Override
+ public Set<VirtualHost> getHosts(NetworkId networkId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ Set<HostId> hostIdSet = networkIdHostIdSetMap.get(networkId);
+ Set<VirtualHost> virtualHostSet = new HashSet<>();
+ if (hostIdSet != null) {
+ hostIdSet.forEach(hostId -> virtualHostSet.add(hostIdVirtualHostMap.get(hostId)));
+ }
+ return ImmutableSet.copyOf(virtualHostSet);
+ }
+
+ @Override
+ public Set<VirtualLink> getLinks(NetworkId networkId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
+ if (virtualLinkSet == null) {
+ virtualLinkSet = new HashSet<>();
+ }
+ return ImmutableSet.copyOf(virtualLinkSet);
+ }
+
+ @Override
+ public VirtualLink getLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+ Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
+ if (virtualLinkSet == null) {
+ return null;
+ }
+
+ VirtualLink virtualLink = null;
+ for (VirtualLink link : virtualLinkSet) {
+ if (src == null && link.dst().equals(dst)) {
+ virtualLink = link;
+ break;
+ } else if (dst == null && link.src().equals(src)) {
+ virtualLink = link;
+ break;
+ } else if (link.src().equals(src) && link.dst().equals(dst)) {
+ virtualLink = link;
+ break;
+ }
+ }
+ return virtualLink;
+ }
+
+ @Override
+ public Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId) {
+ checkState(networkExists(networkId), "The network has not been added.");
+ Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+ if (virtualPortSet == null) {
+ virtualPortSet = new HashSet<>();
+ }
+
+ if (deviceId == null) {
+ return ImmutableSet.copyOf(virtualPortSet);
+ }
+
+ Set<VirtualPort> portSet = new HashSet<>();
+ virtualPortSet.forEach(virtualPort -> {
+ if (virtualPort.element().id().equals(deviceId)) {
+ portSet.add(virtualPort);
+ }
+ });
+ return ImmutableSet.copyOf(portSet);
+ }
+
+ @Override
+ public void addTunnelId(Intent intent, TunnelId tunnelId) {
+ // Add the tunnelId to the intent key set map
+ Set<TunnelId> tunnelIdSet = intentKeyTunnelIdSetMap.remove(intent.key());
+ if (tunnelIdSet == null) {
+ tunnelIdSet = new HashSet<>();
+ }
+ tunnelIdSet.add(tunnelId);
+ intentKeyTunnelIdSetMap.put(intent.key(), tunnelIdSet);
+ }
+
+ @Override
+ public Set<TunnelId> getTunnelIds(Intent intent) {
+ Set<TunnelId> tunnelIdSet = intentKeyTunnelIdSetMap.get(intent.key());
+ return tunnelIdSet == null ? new HashSet<TunnelId>() : ImmutableSet.copyOf(tunnelIdSet);
+ }
+
+ @Override
+ public void removeTunnelId(Intent intent, TunnelId tunnelId) {
+ Set<TunnelId> tunnelIdSet = new HashSet<>();
+ intentKeyTunnelIdSetMap.get(intent.key()).forEach(tunnelId1 -> {
+ if (tunnelId1.equals(tunnelId)) {
+ tunnelIdSet.add(tunnelId);
+ }
+ });
+
+ if (!tunnelIdSet.isEmpty()) {
+ intentKeyTunnelIdSetMap.compute(intent.key(), (key, existingTunnelIds) -> {
+ if (existingTunnelIds == null || existingTunnelIds.isEmpty()) {
+ return new HashSet<>();
+ } else {
+ return new HashSet<>(Sets.difference(existingTunnelIds, tunnelIdSet));
+ }
+ });
+ }
+ }
+
+ /**
+ * Listener class to map listener set events to the virtual network events.
+ */
+ private class InternalSetListener implements SetEventListener<TenantId> {
+ @Override
+ public void event(SetEvent<TenantId> event) {
+ VirtualNetworkEvent.Type type = null;
+ switch (event.type()) {
+ case ADD:
+ type = VirtualNetworkEvent.Type.TENANT_REGISTERED;
+ break;
+ case REMOVE:
+ type = VirtualNetworkEvent.Type.TENANT_UNREGISTERED;
+ break;
+ default:
+ log.error("Unsupported event type: " + event.type());
+ }
+ notifyDelegate(new VirtualNetworkEvent(type, null));
+ }
+ }
+
+ /**
+ * Listener class to map listener map events to the virtual network events.
+ */
+ private class InternalMapListener<K, V> implements MapEventListener<K, V> {
+
+ private final BiFunction<MapEvent.Type, V, VirtualNetworkEvent> createEvent;
+
+ InternalMapListener(BiFunction<MapEvent.Type, V, VirtualNetworkEvent> createEvent) {
+ this.createEvent = createEvent;
+ }
+
+ @Override
+ public void event(MapEvent<K, V> event) {
+ checkNotNull(event.key());
+ VirtualNetworkEvent vnetEvent = null;
+ switch (event.type()) {
+ case INSERT:
+ vnetEvent = createEvent.apply(event.type(), event.newValue().value());
+ break;
+ case UPDATE:
+ if ((event.oldValue().value() != null) && (event.newValue().value() == null)) {
+ vnetEvent = createEvent.apply(MapEvent.Type.REMOVE, event.oldValue().value());
+ } else {
+ vnetEvent = createEvent.apply(event.type(), event.newValue().value());
+ }
+ break;
+ case REMOVE:
+ if (event.oldValue() != null) {
+ vnetEvent = createEvent.apply(event.type(), event.oldValue().value());
+ }
+ break;
+ default:
+ log.error("Unsupported event type: " + event.type());
+ }
+ if (vnetEvent != null) {
+ notifyDelegate(vnetEvent);
+ }
+ }
+ }
+
+ /**
+ * A wrapper class to isolate device id from other virtual networks.
+ */
+
+ private static class VirtualDeviceId {
+
+ NetworkId networkId;
+ DeviceId deviceId;
+
+ public VirtualDeviceId(NetworkId networkId, DeviceId deviceId) {
+ this.networkId = networkId;
+ this.deviceId = deviceId;
+ }
+
+ public NetworkId getNetworkId() {
+ return networkId;
+ }
+
+ public DeviceId getDeviceId() {
+ return deviceId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, deviceId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof VirtualDeviceId) {
+ VirtualDeviceId that = (VirtualDeviceId) obj;
+ return this.deviceId.equals(that.deviceId) &&
+ this.networkId.equals(that.networkId);
+ }
+ return false;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualPacketStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualPacketStore.java
new file mode 100644
index 0000000..16636a5
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/DistributedVirtualPacketStore.java
@@ -0,0 +1,413 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketStoreDelegate;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.incubator.net.virtual.store.impl.OsgiPropertyConstants.MESSAGE_HANDLER_THREAD_POOL_SIZE;
+import static org.onosproject.incubator.net.virtual.store.impl.OsgiPropertyConstants.MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT;
+import static org.onosproject.net.packet.PacketEvent.Type.EMIT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Distributed virtual packet store implementation allowing packets to be sent to
+ * remote instances. Implementation is based on DistributedPacketStore class.
+ */
+@Component(immediate = true, enabled = false, service = VirtualNetworkPacketStore.class,
+ property = {
+ MESSAGE_HANDLER_THREAD_POOL_SIZE + ":Integer=" + MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT,
+ })
+public class DistributedVirtualPacketStore
+ extends AbstractVirtualStore<PacketEvent, PacketStoreDelegate>
+ implements VirtualNetworkPacketStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String FORMAT = "Setting: messageHandlerThreadPoolSize={}";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterCommunicationService communicationService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ComponentConfigService cfgService;
+
+ private PacketRequestTracker tracker;
+
+ private static final MessageSubject PACKET_OUT_SUBJECT =
+ new MessageSubject("virtual-packet-out");
+
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API);
+
+ private ExecutorService messageHandlingExecutor;
+
+ /** Size of thread pool to assign message handler. */
+ private static int messageHandlerThreadPoolSize = MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT;
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+
+ modified(context);
+
+ messageHandlingExecutor = Executors.newFixedThreadPool(
+ messageHandlerThreadPoolSize,
+ groupedThreads("onos/store/packet", "message-handlers", log));
+
+ communicationService.<OutboundPacketWrapper>addSubscriber(PACKET_OUT_SUBJECT,
+ SERIALIZER::decode,
+ packetWrapper -> notifyDelegate(packetWrapper.networkId,
+ new PacketEvent(EMIT,
+ packetWrapper.outboundPacket)),
+ messageHandlingExecutor);
+
+ tracker = new PacketRequestTracker();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+ communicationService.removeSubscriber(PACKET_OUT_SUBJECT);
+ messageHandlingExecutor.shutdown();
+ tracker = null;
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+ int newMessageHandlerThreadPoolSize;
+
+ try {
+ String s = get(properties, MESSAGE_HANDLER_THREAD_POOL_SIZE);
+
+ newMessageHandlerThreadPoolSize =
+ isNullOrEmpty(s) ? messageHandlerThreadPoolSize : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException e) {
+ log.warn(e.getMessage());
+ newMessageHandlerThreadPoolSize = messageHandlerThreadPoolSize;
+ }
+
+ // Any change in the following parameters implies thread pool restart
+ if (newMessageHandlerThreadPoolSize != messageHandlerThreadPoolSize) {
+ setMessageHandlerThreadPoolSize(newMessageHandlerThreadPoolSize);
+ restartMessageHandlerThreadPool();
+ }
+
+ log.info(FORMAT, messageHandlerThreadPoolSize);
+ }
+
+ @Override
+ public void emit(NetworkId networkId, OutboundPacket packet) {
+ NodeId myId = clusterService.getLocalNode().id();
+ // TODO revive this when there is MastershipService support for virtual devices
+// NodeId master = mastershipService.getMasterFor(packet.sendThrough());
+//
+// if (master == null) {
+// log.warn("No master found for {}", packet.sendThrough());
+// return;
+// }
+//
+// log.debug("master {} found for {}", myId, packet.sendThrough());
+// if (myId.equals(master)) {
+// notifyDelegate(networkId, new PacketEvent(EMIT, packet));
+// return;
+// }
+//
+// communicationService.unicast(packet, PACKET_OUT_SUBJECT, SERIALIZER::encode, master)
+// .whenComplete((r, error) -> {
+// if (error != null) {
+// log.warn("Failed to send packet-out to {}", master, error);
+// }
+// });
+ }
+
+ @Override
+ public void requestPackets(NetworkId networkId, PacketRequest request) {
+ tracker.add(networkId, request);
+
+ }
+
+ @Override
+ public void cancelPackets(NetworkId networkId, PacketRequest request) {
+ tracker.remove(networkId, request);
+ }
+
+ @Override
+ public List<PacketRequest> existingRequests(NetworkId networkId) {
+ return tracker.requests(networkId);
+ }
+
+ private final class PacketRequestTracker {
+
+ private ConsistentMap<NetworkId, Map<RequestKey, Set<PacketRequest>>> distRequests;
+ private Map<NetworkId, Map<RequestKey, Set<PacketRequest>>> requests;
+
+ private PacketRequestTracker() {
+ distRequests = storageService.<NetworkId, Map<RequestKey, Set<PacketRequest>>>consistentMapBuilder()
+ .withName("onos-virtual-packet-requests")
+ .withSerializer(Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(RequestKey.class)
+ .register(NetworkId.class)
+ .build()))
+ .build();
+ requests = distRequests.asJavaMap();
+ }
+
+ private void add(NetworkId networkId, PacketRequest request) {
+ AtomicBoolean firstRequest = addInternal(networkId, request);
+ PacketStoreDelegate delegate = delegateMap.get(networkId);
+ if (firstRequest.get() && delegate != null) {
+ // The instance that makes the first request will push to all devices
+ delegate.requestPackets(request);
+ }
+ }
+
+ private AtomicBoolean addInternal(NetworkId networkId, PacketRequest request) {
+ AtomicBoolean firstRequest = new AtomicBoolean(false);
+ AtomicBoolean changed = new AtomicBoolean(true);
+ Map<RequestKey, Set<PacketRequest>> requestsForNetwork = getMap(networkId);
+ requestsForNetwork.compute(key(request), (s, existingRequests) -> {
+ // Reset to false just in case this is a retry due to
+ // ConcurrentModificationException
+ firstRequest.set(false);
+ if (existingRequests == null) {
+ firstRequest.set(true);
+ return ImmutableSet.of(request);
+ } else if (!existingRequests.contains(request)) {
+ firstRequest.set(true);
+ return ImmutableSet.<PacketRequest>builder()
+ .addAll(existingRequests)
+ .add(request)
+ .build();
+ } else {
+ changed.set(false);
+ return existingRequests;
+ }
+ });
+ if (changed.get()) {
+ requests.put(networkId, requestsForNetwork);
+ }
+ return firstRequest;
+ }
+
+ private void remove(NetworkId networkId, PacketRequest request) {
+ AtomicBoolean removedLast = removeInternal(networkId, request);
+ PacketStoreDelegate delegate = delegateMap.get(networkId);
+ if (removedLast.get() && delegate != null) {
+ // The instance that removes the last request will remove from all devices
+ delegate.cancelPackets(request);
+ }
+ }
+
+ private AtomicBoolean removeInternal(NetworkId networkId, PacketRequest request) {
+ AtomicBoolean removedLast = new AtomicBoolean(false);
+ AtomicBoolean changed = new AtomicBoolean(true);
+ Map<RequestKey, Set<PacketRequest>> requestsForNetwork = getMap(networkId);
+ requestsForNetwork.computeIfPresent(key(request), (s, existingRequests) -> {
+ // Reset to false just in case this is a retry due to
+ // ConcurrentModificationException
+ removedLast.set(false);
+ if (existingRequests.contains(request)) {
+ Set<PacketRequest> newRequests = Sets.newHashSet(existingRequests);
+ newRequests.remove(request);
+ if (newRequests.size() > 0) {
+ return ImmutableSet.copyOf(newRequests);
+ } else {
+ removedLast.set(true);
+ return null;
+ }
+ } else {
+ changed.set(false);
+ return existingRequests;
+ }
+ });
+ if (changed.get()) {
+ requests.put(networkId, requestsForNetwork);
+ }
+ return removedLast;
+ }
+
+ private List<PacketRequest> requests(NetworkId networkId) {
+ Map<RequestKey, Set<PacketRequest>> requestsForNetwork = getMap(networkId);
+ List<PacketRequest> list = Lists.newArrayList();
+ requestsForNetwork.values().forEach(v -> list.addAll(v));
+ list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue());
+ return list;
+ }
+
+ /*
+ * Gets PacketRequests for specified networkId.
+ */
+ private Map<RequestKey, Set<PacketRequest>> getMap(NetworkId networkId) {
+ return requests.computeIfAbsent(networkId, networkId1 -> {
+ log.debug("Creating new map for {}", networkId1);
+ Map newMap = Maps.newHashMap();
+ return newMap;
+ });
+ }
+ }
+
+ /**
+ * Creates a new request key from a packet request.
+ *
+ * @param request packet request
+ * @return request key
+ */
+ private static RequestKey key(PacketRequest request) {
+ return new RequestKey(request.selector(), request.priority());
+ }
+
+ /**
+ * Key of a packet request.
+ */
+ private static final class RequestKey {
+ private final TrafficSelector selector;
+ private final PacketPriority priority;
+
+ private RequestKey(TrafficSelector selector, PacketPriority priority) {
+ this.selector = selector;
+ this.priority = priority;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(selector, priority);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof RequestKey)) {
+ return false;
+ }
+
+ RequestKey that = (RequestKey) other;
+
+ return Objects.equals(selector, that.selector) &&
+ Objects.equals(priority, that.priority);
+ }
+ }
+
+ private static OutboundPacketWrapper wrapper(NetworkId networkId, OutboundPacket outboundPacket) {
+ return new OutboundPacketWrapper(networkId, outboundPacket);
+ }
+
+ /*
+ * OutboundPacket in
+ */
+ private static final class OutboundPacketWrapper {
+ private NetworkId networkId;
+ private OutboundPacket outboundPacket;
+
+ private OutboundPacketWrapper(NetworkId networkId, OutboundPacket outboundPacket) {
+ this.networkId = networkId;
+ this.outboundPacket = outboundPacket;
+ }
+
+ }
+
+ /**
+ * Sets thread pool size of message handler.
+ *
+ * @param poolSize
+ */
+ private void setMessageHandlerThreadPoolSize(int poolSize) {
+ checkArgument(poolSize >= 0, "Message handler pool size must be 0 or more");
+ messageHandlerThreadPoolSize = poolSize;
+ }
+
+ /**
+ * Restarts thread pool of message handler.
+ */
+ private void restartMessageHandlerThreadPool() {
+ ExecutorService prevExecutor = messageHandlingExecutor;
+ messageHandlingExecutor = newFixedThreadPool(getMessageHandlerThreadPoolSize(),
+ groupedThreads("DistPktStore", "messageHandling-%d", log));
+ prevExecutor.shutdown();
+ }
+
+ /**
+ * Gets current thread pool size of message handler.
+ *
+ * @return messageHandlerThreadPoolSize
+ */
+ private int getMessageHandlerThreadPoolSize() {
+ return messageHandlerThreadPoolSize;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/MeterData.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/MeterData.java
new file mode 100644
index 0000000..c014505
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/MeterData.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-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.store.impl;
+
+import org.onosproject.cluster.NodeId;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterFailReason;
+
+import java.util.Optional;
+
+/**
+ * A class representing the meter information stored in the meter store.
+ */
+public class MeterData {
+
+ private final Meter meter;
+ private final Optional<MeterFailReason> reason;
+ private final NodeId origin;
+
+ public MeterData(Meter meter, MeterFailReason reason, NodeId origin) {
+ this.meter = meter;
+ this.reason = Optional.ofNullable(reason);
+ this.origin = origin;
+ }
+
+ public Meter meter() {
+ return meter;
+ }
+
+ public Optional<MeterFailReason> reason() {
+ return this.reason;
+ }
+
+ public NodeId origin() {
+ return this.origin;
+ }
+
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/OsgiPropertyConstants.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/OsgiPropertyConstants.java
new file mode 100644
index 0000000..39bc5b0
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/OsgiPropertyConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018-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.store.impl;
+
+/**
+ * Constants for default values of configurable properties.
+ */
+public final class OsgiPropertyConstants {
+
+ private OsgiPropertyConstants() {}
+
+ public static final String MESSAGE_HANDLER_THREAD_POOL_SIZE = "messageHandlerThreadPoolSize";
+ public static final int MESSAGE_HANDLER_THREAD_POOL_SIZE_DEFAULT = 4;
+
+ public static final String BACKUP_PERIOD_MILLIS = "backupPeriod";
+ public static final int BACKUP_PERIOD_MILLIS_DEFAULT = 2000;
+
+ public static final String PERSISTENCE_ENABLED = "persistenceEnabled";
+ public static final boolean PERSISTENCE_ENABLED_DEFAULT = false;
+
+ public static final String PENDING_FUTURE_TIMEOUT_MINUTES = "pendingFutureTimeoutMinutes";
+ public static final int PENDING_FUTURE_TIMEOUT_MINUTES_DEFAULT = 5;
+
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowObjectiveStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowObjectiveStore.java
new file mode 100644
index 0000000..4ef268e
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowObjectiveStore.java
@@ -0,0 +1,172 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.Maps;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.net.behaviour.DefaultNextGroup;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.store.service.AtomicCounter;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Single instance implementation of store to manage
+ * the inventory of created next groups for virtual network.
+ */
+@Component(immediate = true, service = VirtualNetworkFlowObjectiveStore.class)
+public class SimpleVirtualFlowObjectiveStore
+ extends AbstractVirtualStore<ObjectiveEvent, FlowObjectiveStoreDelegate>
+ implements VirtualNetworkFlowObjectiveStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private ConcurrentMap<NetworkId, ConcurrentMap<Integer, byte[]>> nextGroupsMap;
+
+ private AtomicCounter nextIds;
+
+ // event queue to separate map-listener threads from event-handler threads (tpool)
+ private BlockingQueue<VirtualObjectiveEvent> eventQ;
+ private ExecutorService tpool;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected StorageService storageService;
+
+ @Activate
+ public void activate() {
+ tpool = Executors.newFixedThreadPool(4, groupedThreads("onos/virtual/flobj-notifier", "%d", log));
+ eventQ = new LinkedBlockingQueue<>();
+ tpool.execute(new FlowObjectiveNotifier());
+
+ initNextGroupsMap();
+
+ nextIds = storageService.getAtomicCounter("next-objective-counter");
+ log.info("Started");
+ }
+
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ protected void initNextGroupsMap() {
+ nextGroupsMap = Maps.newConcurrentMap();
+ }
+
+ protected void updateNextGroupsMap(NetworkId networkId,
+ ConcurrentMap<Integer, byte[]> nextGroups) {
+ }
+
+ protected ConcurrentMap<Integer, byte[]> getNextGroups(NetworkId networkId) {
+ nextGroupsMap.computeIfAbsent(networkId, n -> Maps.newConcurrentMap());
+ return nextGroupsMap.get(networkId);
+ }
+
+ @Override
+ public void putNextGroup(NetworkId networkId, Integer nextId, NextGroup group) {
+ ConcurrentMap<Integer, byte[]> nextGroups = getNextGroups(networkId);
+ nextGroups.put(nextId, group.data());
+ updateNextGroupsMap(networkId, nextGroups);
+
+ eventQ.add(new VirtualObjectiveEvent(networkId, ObjectiveEvent.Type.ADD, nextId));
+ }
+
+ @Override
+ public NextGroup getNextGroup(NetworkId networkId, Integer nextId) {
+ ConcurrentMap<Integer, byte[]> nextGroups = getNextGroups(networkId);
+ byte[] groupData = nextGroups.get(nextId);
+ if (groupData != null) {
+ return new DefaultNextGroup(groupData);
+ }
+ return null;
+ }
+
+ @Override
+ public NextGroup removeNextGroup(NetworkId networkId, Integer nextId) {
+ ConcurrentMap<Integer, byte[]> nextGroups = getNextGroups(networkId);
+ byte[] nextGroup = nextGroups.remove(nextId);
+ updateNextGroupsMap(networkId, nextGroups);
+
+ eventQ.add(new VirtualObjectiveEvent(networkId, ObjectiveEvent.Type.REMOVE, nextId));
+
+ return new DefaultNextGroup(nextGroup);
+ }
+
+ @Override
+ public Map<Integer, NextGroup> getAllGroups(NetworkId networkId) {
+ ConcurrentMap<Integer, byte[]> nextGroups = getNextGroups(networkId);
+
+ Map<Integer, NextGroup> nextGroupMappings = new HashMap<>();
+ for (int key : nextGroups.keySet()) {
+ NextGroup nextGroup = getNextGroup(networkId, key);
+ if (nextGroup != null) {
+ nextGroupMappings.put(key, nextGroup);
+ }
+ }
+ return nextGroupMappings;
+ }
+
+ @Override
+ public int allocateNextId(NetworkId networkId) {
+ return (int) nextIds.incrementAndGet();
+ }
+
+ private class FlowObjectiveNotifier implements Runnable {
+ @Override
+ public void run() {
+ try {
+ while (!Thread.currentThread().isInterrupted()) {
+ VirtualObjectiveEvent vEvent = eventQ.take();
+ notifyDelegate(vEvent.networkId(), vEvent);
+ }
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private class VirtualObjectiveEvent extends ObjectiveEvent {
+ NetworkId networkId;
+
+ public VirtualObjectiveEvent(NetworkId networkId, Type type,
+ Integer objective) {
+ super(type, objective);
+ this.networkId = networkId;
+ }
+
+ NetworkId networkId() {
+ return networkId;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowRuleStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowRuleStore.java
new file mode 100644
index 0000000..1fc5cde
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualFlowRuleStore.java
@@ -0,0 +1,411 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.SettableFuture;
+import org.onlab.util.Tools;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchRequest;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.onosproject.incubator.net.virtual.store.impl.OsgiPropertyConstants.PENDING_FUTURE_TIMEOUT_MINUTES;
+import static org.onosproject.incubator.net.virtual.store.impl.OsgiPropertyConstants.PENDING_FUTURE_TIMEOUT_MINUTES_DEFAULT;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the virtual network flow rule store to manage inventory of
+ * virtual flow rules using trivial in-memory implementation.
+ */
+//TODO: support distributed flowrule store for virtual networks
+
+@Component(immediate = true, service = VirtualNetworkFlowRuleStore.class,
+ property = {
+ PENDING_FUTURE_TIMEOUT_MINUTES + ":Integer=" + PENDING_FUTURE_TIMEOUT_MINUTES_DEFAULT,
+ })
+public class SimpleVirtualFlowRuleStore
+ extends AbstractVirtualStore<FlowRuleBatchEvent, FlowRuleStoreDelegate>
+ implements VirtualNetworkFlowRuleStore {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected StorageService storageService;
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>>>
+ flowEntries = new ConcurrentHashMap<>();
+
+
+ private final AtomicInteger localBatchIdGen = new AtomicInteger();
+
+ /** Expiration time after an entry is created that it should be automatically removed. */
+ private int pendingFutureTimeoutMinutes = PENDING_FUTURE_TIMEOUT_MINUTES_DEFAULT;
+
+ private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
+ CacheBuilder.newBuilder()
+ .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
+ .removalListener(new TimeoutFuture())
+ .build();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ flowEntries.clear();
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+
+ readComponentConfiguration(context);
+
+ // Reset Cache and copy all.
+ Cache<Integer, SettableFuture<CompletedBatchOperation>> prevFutures = pendingFutures;
+ pendingFutures = CacheBuilder.newBuilder()
+ .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
+ .removalListener(new TimeoutFuture())
+ .build();
+
+ pendingFutures.putAll(prevFutures.asMap());
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+
+ Integer newPendingFutureTimeoutMinutes =
+ Tools.getIntegerProperty(properties, "pendingFutureTimeoutMinutes");
+ if (newPendingFutureTimeoutMinutes == null) {
+ pendingFutureTimeoutMinutes = PENDING_FUTURE_TIMEOUT_MINUTES_DEFAULT;
+ log.info("Pending future timeout is not configured, " +
+ "using current value of {}", pendingFutureTimeoutMinutes);
+ } else {
+ pendingFutureTimeoutMinutes = newPendingFutureTimeoutMinutes;
+ log.info("Configured. Pending future timeout is configured to {}",
+ pendingFutureTimeoutMinutes);
+ }
+ }
+
+ @Override
+ public int getFlowRuleCount(NetworkId networkId) {
+ int sum = 0;
+
+ if (flowEntries.get(networkId) == null) {
+ return 0;
+ }
+
+ for (ConcurrentMap<FlowId, List<StoredFlowEntry>> ft :
+ flowEntries.get(networkId).values()) {
+ for (List<StoredFlowEntry> fes : ft.values()) {
+ sum += fes.size();
+ }
+ }
+ return sum;
+ }
+
+ @Override
+ public FlowEntry getFlowEntry(NetworkId networkId, FlowRule rule) {
+ return getFlowEntryInternal(networkId, rule.deviceId(), rule);
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(NetworkId networkId, DeviceId deviceId) {
+ return FluentIterable.from(getFlowTable(networkId, deviceId).values())
+ .transformAndConcat(Collections::unmodifiableList);
+ }
+
+ private void storeFlowRule(NetworkId networkId, FlowRule rule) {
+ storeFlowRuleInternal(networkId, rule);
+ }
+
+ @Override
+ public void storeBatch(NetworkId networkId, FlowRuleBatchOperation batchOperation) {
+ List<FlowRuleBatchEntry> toAdd = new ArrayList<>();
+ List<FlowRuleBatchEntry> toRemove = new ArrayList<>();
+
+ for (FlowRuleBatchEntry entry : batchOperation.getOperations()) {
+ final FlowRule flowRule = entry.target();
+ if (entry.operator().equals(FlowRuleBatchEntry.FlowRuleOperation.ADD)) {
+ if (!getFlowEntries(networkId, flowRule.deviceId(),
+ flowRule.id()).contains(flowRule)) {
+ storeFlowRule(networkId, flowRule);
+ toAdd.add(entry);
+ }
+ } else if (entry.operator().equals(FlowRuleBatchEntry.FlowRuleOperation.REMOVE)) {
+ if (getFlowEntries(networkId, flowRule.deviceId(), flowRule.id()).contains(flowRule)) {
+ deleteFlowRule(networkId, flowRule);
+ toRemove.add(entry);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported operation type");
+ }
+ }
+
+ if (toAdd.isEmpty() && toRemove.isEmpty()) {
+ notifyDelegate(networkId, FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(batchOperation.id(), Collections.emptySet()),
+ new CompletedBatchOperation(true, Collections.emptySet(),
+ batchOperation.deviceId())));
+ return;
+ }
+
+ SettableFuture<CompletedBatchOperation> r = SettableFuture.create();
+ final int futureId = localBatchIdGen.incrementAndGet();
+
+ pendingFutures.put(futureId, r);
+
+ toAdd.addAll(toRemove);
+ notifyDelegate(networkId, FlowRuleBatchEvent.requested(
+ new FlowRuleBatchRequest(batchOperation.id(),
+ Sets.newHashSet(toAdd)), batchOperation.deviceId()));
+
+ }
+
+ @Override
+ public void batchOperationComplete(NetworkId networkId, FlowRuleBatchEvent event) {
+ final Long batchId = event.subject().batchId();
+ SettableFuture<CompletedBatchOperation> future
+ = pendingFutures.getIfPresent(batchId);
+ if (future != null) {
+ future.set(event.result());
+ pendingFutures.invalidate(batchId);
+ }
+ notifyDelegate(networkId, event);
+ }
+
+ @Override
+ public void deleteFlowRule(NetworkId networkId, FlowRule rule) {
+ List<StoredFlowEntry> entries = getFlowEntries(networkId, rule.deviceId(), rule.id());
+
+ synchronized (entries) {
+ for (StoredFlowEntry entry : entries) {
+ if (entry.equals(rule)) {
+ synchronized (entry) {
+ entry.setState(FlowEntry.FlowEntryState.PENDING_REMOVE);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public FlowRuleEvent addOrUpdateFlowRule(NetworkId networkId, FlowEntry rule) {
+ // check if this new rule is an update to an existing entry
+ List<StoredFlowEntry> entries = getFlowEntries(networkId, rule.deviceId(), rule.id());
+ synchronized (entries) {
+ for (StoredFlowEntry stored : entries) {
+ if (stored.equals(rule)) {
+ synchronized (stored) {
+ //FIXME modification of "stored" flow entry outside of flow table
+ stored.setBytes(rule.bytes());
+ stored.setLife(rule.life());
+ stored.setPackets(rule.packets());
+ if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
+ stored.setState(FlowEntry.FlowEntryState.ADDED);
+ // TODO: Do we need to change `rule` state?
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED, rule);
+ }
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_UPDATED, rule);
+ }
+ }
+ }
+ }
+
+ // should not reach here
+ // storeFlowRule was expected to be called
+ log.error("FlowRule was not found in store {} to update", rule);
+
+ return null;
+ }
+
+ @Override
+ public FlowRuleEvent removeFlowRule(NetworkId networkId, FlowEntry rule) {
+ // This is where one could mark a rule as removed and still keep it in the store.
+ final DeviceId did = rule.deviceId();
+
+ List<StoredFlowEntry> entries = getFlowEntries(networkId, did, rule.id());
+ synchronized (entries) {
+ if (entries.remove(rule)) {
+ return new FlowRuleEvent(RULE_REMOVED, rule);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public FlowRuleEvent pendingFlowRule(NetworkId networkId, FlowEntry rule) {
+ List<StoredFlowEntry> entries = getFlowEntries(networkId, rule.deviceId(), rule.id());
+ synchronized (entries) {
+ for (StoredFlowEntry entry : entries) {
+ if (entry.equals(rule) &&
+ entry.state() != FlowEntry.FlowEntryState.PENDING_ADD) {
+ synchronized (entry) {
+ entry.setState(FlowEntry.FlowEntryState.PENDING_ADD);
+ return new FlowRuleEvent(FlowRuleEvent.Type.RULE_UPDATED, rule);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void purgeFlowRule(NetworkId networkId, DeviceId deviceId) {
+ flowEntries.get(networkId).remove(deviceId);
+ }
+
+ @Override
+ public void purgeFlowRules(NetworkId networkId) {
+ flowEntries.get(networkId).clear();
+ }
+
+ @Override
+ public FlowRuleEvent
+ updateTableStatistics(NetworkId networkId, DeviceId deviceId, List<TableStatisticsEntry> tableStats) {
+ //TODO: Table operations are not supported yet
+ return null;
+ }
+
+ @Override
+ public Iterable<TableStatisticsEntry>
+ getTableStatistics(NetworkId networkId, DeviceId deviceId) {
+ //TODO: Table operations are not supported yet
+ return null;
+ }
+
+ /**
+ * Returns the flow table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the virtual device
+ * @return Map representing Flow Table of given device.
+ */
+ private ConcurrentMap<FlowId, List<StoredFlowEntry>>
+ getFlowTable(NetworkId networkId, DeviceId deviceId) {
+ return flowEntries
+ .computeIfAbsent(networkId, n -> new ConcurrentHashMap<>())
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ private List<StoredFlowEntry>
+ getFlowEntries(NetworkId networkId, DeviceId deviceId, FlowId flowId) {
+ final ConcurrentMap<FlowId, List<StoredFlowEntry>> flowTable
+ = getFlowTable(networkId, deviceId);
+
+ List<StoredFlowEntry> r = flowTable.get(flowId);
+ if (r == null) {
+ final List<StoredFlowEntry> concurrentlyAdded;
+ r = new CopyOnWriteArrayList<>();
+ concurrentlyAdded = flowTable.putIfAbsent(flowId, r);
+ if (concurrentlyAdded != null) {
+ return concurrentlyAdded;
+ }
+ }
+ return r;
+ }
+
+ private FlowEntry
+ getFlowEntryInternal(NetworkId networkId, DeviceId deviceId, FlowRule rule) {
+ List<StoredFlowEntry> fes = getFlowEntries(networkId, deviceId, rule.id());
+ for (StoredFlowEntry fe : fes) {
+ if (fe.equals(rule)) {
+ return fe;
+ }
+ }
+ return null;
+ }
+
+ private void storeFlowRuleInternal(NetworkId networkId, FlowRule rule) {
+ StoredFlowEntry f = new DefaultFlowEntry(rule);
+ final DeviceId did = f.deviceId();
+ final FlowId fid = f.id();
+ List<StoredFlowEntry> existing = getFlowEntries(networkId, did, fid);
+ synchronized (existing) {
+ for (StoredFlowEntry fe : existing) {
+ if (fe.equals(rule)) {
+ // was already there? ignore
+ return;
+ }
+ }
+ // new flow rule added
+ existing.add(f);
+ }
+ }
+
+ private static final class TimeoutFuture
+ implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
+ @Override
+ public void onRemoval(RemovalNotification<Integer,
+ SettableFuture<CompletedBatchOperation>> notification) {
+ // wrapping in ExecutionException to support Future.get
+ if (notification.wasEvicted()) {
+ notification.getValue()
+ .setException(new ExecutionException("Timed out",
+ new TimeoutException()));
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java
new file mode 100644
index 0000000..3c696ce
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java
@@ -0,0 +1,764 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+import org.onosproject.core.GroupId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.group.StoredGroupBucketEntry;
+import org.onosproject.net.group.StoredGroupEntry;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages inventory of virtual group entries using trivial in-memory implementation.
+ */
+@Component(immediate = true, service = VirtualNetworkGroupStore.class)
+public class SimpleVirtualGroupStore
+ extends AbstractVirtualStore<GroupEvent, GroupStoreDelegate>
+ implements VirtualNetworkGroupStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private final int dummyId = 0xffffffff;
+ private final GroupId dummyGroupId = new GroupId(dummyId);
+
+ // inner Map is per device group table
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
+ groupEntriesByKey = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>>
+ groupEntriesById = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
+ pendingGroupEntriesByKey = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>>
+ extraneousGroupEntriesById = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId, HashMap<DeviceId, Boolean>>
+ deviceAuditStatus = new ConcurrentHashMap<>();
+
+ private final AtomicInteger groupIdGen = new AtomicInteger();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ groupEntriesByKey.clear();
+ groupEntriesById.clear();
+ log.info("Stopped");
+ }
+
+ /**
+ * Returns the group key table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupKey, StoredGroupEntry>
+ getGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
+ groupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return groupEntriesByKey.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the group id table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupId, StoredGroupEntry>
+ getGroupIdTable(NetworkId networkId, DeviceId deviceId) {
+ groupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return groupEntriesById.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the pending group key table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupKey, StoredGroupEntry>
+ getPendingGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
+ pendingGroupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return pendingGroupEntriesByKey.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the extraneous group id table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupId, Group>
+ getExtraneousGroupIdTable(NetworkId networkId, DeviceId deviceId) {
+ extraneousGroupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return extraneousGroupEntriesById.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ @Override
+ public int getGroupCount(NetworkId networkId, DeviceId deviceId) {
+ return (groupEntriesByKey.get(networkId).get(deviceId) != null) ?
+ groupEntriesByKey.get(networkId).get(deviceId).size() : 0;
+ }
+
+ @Override
+ public Iterable<Group> getGroups(NetworkId networkId, DeviceId deviceId) {
+ // flatten and make iterator unmodifiable
+ return FluentIterable.from(getGroupKeyTable(networkId, deviceId).values())
+ .transform(input -> input);
+ }
+
+ @Override
+ public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupKey appCookie) {
+ if (groupEntriesByKey.get(networkId) != null &&
+ groupEntriesByKey.get(networkId).get(deviceId) != null) {
+ return groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
+ }
+ return null;
+ }
+
+ @Override
+ public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupId groupId) {
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ return groupEntriesById.get(networkId).get(deviceId).get(groupId);
+ }
+ return null;
+ }
+
+ private int getFreeGroupIdValue(NetworkId networkId, DeviceId deviceId) {
+ int freeId = groupIdGen.incrementAndGet();
+
+ while (true) {
+ Group existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesById.get(networkId).get(deviceId)
+ .get(new GroupId(freeId));
+ }
+
+ if (existing == null) {
+ if (extraneousGroupEntriesById.get(networkId) != null &&
+ extraneousGroupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = extraneousGroupEntriesById.get(networkId).get(deviceId)
+ .get(new GroupId(freeId));
+ }
+ }
+
+ if (existing != null) {
+ freeId = groupIdGen.incrementAndGet();
+ } else {
+ break;
+ }
+ }
+ return freeId;
+ }
+
+ @Override
+ public void storeGroupDescription(NetworkId networkId, GroupDescription groupDesc) {
+ // Check if a group is existing with the same key
+ if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
+ return;
+ }
+
+ if (deviceAuditStatus.get(networkId) == null ||
+ deviceAuditStatus.get(networkId).get(groupDesc.deviceId()) == null) {
+ // Device group audit has not completed yet
+ // Add this group description to pending group key table
+ // Create a group entry object with Dummy Group ID
+ StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
+ group.setState(Group.GroupState.WAITING_AUDIT_COMPLETE);
+ ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
+ getPendingGroupKeyTable(networkId, groupDesc.deviceId());
+ pendingKeyTable.put(groupDesc.appCookie(), group);
+ return;
+ }
+
+ storeGroupDescriptionInternal(networkId, groupDesc);
+ }
+
+ private void storeGroupDescriptionInternal(NetworkId networkId,
+ GroupDescription groupDesc) {
+ // Check if a group is existing with the same key
+ if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
+ return;
+ }
+
+ GroupId id = null;
+ if (groupDesc.givenGroupId() == null) {
+ // Get a new group identifier
+ id = new GroupId(getFreeGroupIdValue(networkId, groupDesc.deviceId()));
+ } else {
+ id = new GroupId(groupDesc.givenGroupId());
+ }
+ // Create a group entry object
+ StoredGroupEntry group = new DefaultGroup(id, groupDesc);
+ // Insert the newly created group entry into concurrent key and id maps
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, groupDesc.deviceId());
+ keyTable.put(groupDesc.appCookie(), group);
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, groupDesc.deviceId());
+ idTable.put(id, group);
+ notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
+ group));
+ }
+
+ @Override
+ public void updateGroupDescription(NetworkId networkId, DeviceId deviceId,
+ GroupKey oldAppCookie, UpdateType type,
+ GroupBuckets newBuckets, GroupKey newAppCookie) {
+ // Check if a group is existing with the provided key
+ Group oldGroup = getGroup(networkId, deviceId, oldAppCookie);
+ if (oldGroup == null) {
+ return;
+ }
+
+ List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
+ type,
+ newBuckets);
+ if (newBucketList != null) {
+ // Create a new group object from the old group
+ GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
+ GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
+ GroupDescription updatedGroupDesc = new DefaultGroupDescription(
+ oldGroup.deviceId(),
+ oldGroup.type(),
+ updatedBuckets,
+ newCookie,
+ oldGroup.givenGroupId(),
+ oldGroup.appId());
+ StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
+ updatedGroupDesc);
+ newGroup.setState(Group.GroupState.PENDING_UPDATE);
+ newGroup.setLife(oldGroup.life());
+ newGroup.setPackets(oldGroup.packets());
+ newGroup.setBytes(oldGroup.bytes());
+
+ // Remove the old entry from maps and add new entry using new key
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, oldGroup.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, oldGroup.deviceId());
+ keyTable.remove(oldGroup.appCookie());
+ idTable.remove(oldGroup.id());
+ keyTable.put(newGroup.appCookie(), newGroup);
+ idTable.put(newGroup.id(), newGroup);
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_UPDATE_REQUESTED,
+ newGroup));
+ }
+
+ }
+
+ private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
+ UpdateType type,
+ GroupBuckets buckets) {
+ if (type == UpdateType.SET) {
+ return buckets.buckets();
+ }
+
+ List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
+ List<GroupBucket> updatedBucketList = new ArrayList<>();
+ boolean groupDescUpdated = false;
+
+ if (type == UpdateType.ADD) {
+ List<GroupBucket> newBuckets = buckets.buckets();
+
+ // Add old buckets that will not be updated and check if any will be updated.
+ for (GroupBucket oldBucket : oldBuckets) {
+ int newBucketIndex = newBuckets.indexOf(oldBucket);
+
+ if (newBucketIndex != -1) {
+ GroupBucket newBucket = newBuckets.get(newBucketIndex);
+ if (!newBucket.hasSameParameters(oldBucket)) {
+ // Bucket will be updated
+ groupDescUpdated = true;
+ }
+ } else {
+ // Old bucket will remain the same - add it.
+ updatedBucketList.add(oldBucket);
+ }
+ }
+
+ // Add all new buckets
+ updatedBucketList.addAll(newBuckets);
+ if (!oldBuckets.containsAll(newBuckets)) {
+ groupDescUpdated = true;
+ }
+
+ } else if (type == UpdateType.REMOVE) {
+ List<GroupBucket> bucketsToRemove = buckets.buckets();
+
+ // Check which old buckets should remain
+ for (GroupBucket oldBucket : oldBuckets) {
+ if (!bucketsToRemove.contains(oldBucket)) {
+ updatedBucketList.add(oldBucket);
+ } else {
+ groupDescUpdated = true;
+ }
+ }
+ }
+
+ if (groupDescUpdated) {
+ return updatedBucketList;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteGroupDescription(NetworkId networkId, DeviceId deviceId,
+ GroupKey appCookie) {
+ // Check if a group is existing with the provided key
+ StoredGroupEntry existing = null;
+ if (groupEntriesByKey.get(networkId) != null &&
+ groupEntriesByKey.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
+ }
+
+ if (existing == null) {
+ return;
+ }
+
+ synchronized (existing) {
+ existing.setState(Group.GroupState.PENDING_DELETE);
+ }
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, existing));
+ }
+
+ @Override
+ public void addOrUpdateGroupEntry(NetworkId networkId, Group group) {
+ // check if this new entry is an update to an existing entry
+ StoredGroupEntry existing = null;
+
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+ existing = groupEntriesById
+ .get(networkId)
+ .get(group.deviceId())
+ .get(group.id());
+ }
+
+ GroupEvent event = null;
+
+ if (existing != null) {
+ synchronized (existing) {
+ for (GroupBucket bucket:group.buckets().buckets()) {
+ Optional<GroupBucket> matchingBucket =
+ existing.buckets().buckets()
+ .stream()
+ .filter((existingBucket) -> (existingBucket.equals(bucket)))
+ .findFirst();
+ if (matchingBucket.isPresent()) {
+ ((StoredGroupBucketEntry) matchingBucket.
+ get()).setPackets(bucket.packets());
+ ((StoredGroupBucketEntry) matchingBucket.
+ get()).setBytes(bucket.bytes());
+ } else {
+ log.warn("addOrUpdateGroupEntry: No matching "
+ + "buckets to update stats");
+ }
+ }
+ existing.setLife(group.life());
+ existing.setPackets(group.packets());
+ existing.setBytes(group.bytes());
+ if (existing.state() == Group.GroupState.PENDING_ADD) {
+ existing.setState(Group.GroupState.ADDED);
+ event = new GroupEvent(GroupEvent.Type.GROUP_ADDED, existing);
+ } else {
+ if (existing.state() == Group.GroupState.PENDING_UPDATE) {
+ existing.setState(Group.GroupState.ADDED);
+ }
+ event = new GroupEvent(GroupEvent.Type.GROUP_UPDATED, existing);
+ }
+ }
+ }
+
+ if (event != null) {
+ notifyDelegate(networkId, event);
+ }
+ }
+
+ @Override
+ public void removeGroupEntry(NetworkId networkId, Group group) {
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null
+ && groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+ existing = groupEntriesById
+ .get(networkId).get(group.deviceId()).get(group.id());
+ }
+
+ if (existing != null) {
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, existing.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, existing.deviceId());
+ idTable.remove(existing.id());
+ keyTable.remove(existing.appCookie());
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED, existing));
+ }
+ }
+
+ @Override
+ public void purgeGroupEntry(NetworkId networkId, DeviceId deviceId) {
+ if (groupEntriesById.get(networkId) != null) {
+ Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
+ groupEntriesById.get(networkId).get(deviceId).entrySet();
+ groupEntriesById.get(networkId).remove(deviceId);
+ groupEntriesByKey.get(networkId).remove(deviceId);
+
+ entryPendingRemove.forEach(entry -> {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
+ entry.getValue()));
+ });
+ }
+ }
+
+ @Override
+ public void purgeGroupEntries(NetworkId networkId) {
+ if (groupEntriesById.get(networkId) != null) {
+ groupEntriesById.get((networkId)).values().forEach(groupEntries -> {
+ groupEntries.entrySet().forEach(entry -> {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
+ entry.getValue()));
+ });
+ });
+
+ groupEntriesById.get(networkId).clear();
+ groupEntriesByKey.get(networkId).clear();
+ }
+ }
+
+ @Override
+ public void addOrUpdateExtraneousGroupEntry(NetworkId networkId, Group group) {
+ ConcurrentMap<GroupId, Group> extraneousIdTable =
+ getExtraneousGroupIdTable(networkId, group.deviceId());
+ extraneousIdTable.put(group.id(), group);
+ // Check the reference counter
+ if (group.referenceCount() == 0) {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, group));
+ }
+ }
+
+ @Override
+ public void removeExtraneousGroupEntry(NetworkId networkId, Group group) {
+ ConcurrentMap<GroupId, Group> extraneousIdTable =
+ getExtraneousGroupIdTable(networkId, group.deviceId());
+ extraneousIdTable.remove(group.id());
+ }
+
+ @Override
+ public Iterable<Group> getExtraneousGroups(NetworkId networkId, DeviceId deviceId) {
+ // flatten and make iterator unmodifiable
+ return FluentIterable.from(
+ getExtraneousGroupIdTable(networkId, deviceId).values());
+ }
+
+ @Override
+ public void deviceInitialAuditCompleted(NetworkId networkId, DeviceId deviceId,
+ boolean completed) {
+ deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
+
+ HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
+ deviceAuditStatus.get(networkId);
+
+ synchronized (deviceAuditStatusByNetwork) {
+ if (completed) {
+ log.debug("deviceInitialAuditCompleted: AUDIT "
+ + "completed for device {}", deviceId);
+ deviceAuditStatusByNetwork.put(deviceId, true);
+ // Execute all pending group requests
+ ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
+ getPendingGroupKeyTable(networkId, deviceId);
+ for (Group group:pendingGroupRequests.values()) {
+ GroupDescription tmp = new DefaultGroupDescription(
+ group.deviceId(),
+ group.type(),
+ group.buckets(),
+ group.appCookie(),
+ group.givenGroupId(),
+ group.appId());
+ storeGroupDescriptionInternal(networkId, tmp);
+ }
+ getPendingGroupKeyTable(networkId, deviceId).clear();
+ } else {
+ if (deviceAuditStatusByNetwork.get(deviceId)) {
+ log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
+ + "status for device {}", deviceId);
+ deviceAuditStatusByNetwork.put(deviceId, false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean deviceInitialAuditStatus(NetworkId networkId, DeviceId deviceId) {
+ deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
+
+ HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
+ deviceAuditStatus.get(networkId);
+
+ synchronized (deviceAuditStatusByNetwork) {
+ return (deviceAuditStatusByNetwork.get(deviceId) != null)
+ ? deviceAuditStatusByNetwork.get(deviceId) : false;
+ }
+ }
+
+ @Override
+ public void groupOperationFailed(NetworkId networkId, DeviceId deviceId,
+ GroupOperation operation) {
+
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesById.get(networkId).get(deviceId)
+ .get(operation.groupId());
+ }
+
+ if (existing == null) {
+ log.warn("No group entry with ID {} found ", operation.groupId());
+ return;
+ }
+
+ switch (operation.opType()) {
+ case ADD:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_ADD_FAILED,
+ existing));
+ break;
+ case MODIFY:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_UPDATE_FAILED,
+ existing));
+ break;
+ case DELETE:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_FAILED,
+ existing));
+ break;
+ default:
+ log.warn("Unknown group operation type {}", operation.opType());
+ }
+
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, existing.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, existing.deviceId());
+ idTable.remove(existing.id());
+ keyTable.remove(existing.appCookie());
+ }
+
+ @Override
+ public void pushGroupMetrics(NetworkId networkId, DeviceId deviceId,
+ Collection<Group> groupEntries) {
+ boolean deviceInitialAuditStatus =
+ deviceInitialAuditStatus(networkId, deviceId);
+ Set<Group> southboundGroupEntries =
+ Sets.newHashSet(groupEntries);
+ Set<Group> storedGroupEntries =
+ Sets.newHashSet(getGroups(networkId, deviceId));
+ Set<Group> extraneousStoredEntries =
+ Sets.newHashSet(getExtraneousGroups(networkId, deviceId));
+
+ if (log.isTraceEnabled()) {
+ log.trace("pushGroupMetrics: Displaying all ({}) "
+ + "southboundGroupEntries for device {}",
+ southboundGroupEntries.size(),
+ deviceId);
+ for (Group group : southboundGroupEntries) {
+ log.trace("Group {} in device {}", group, deviceId);
+ }
+
+ log.trace("Displaying all ({}) stored group entries for device {}",
+ storedGroupEntries.size(),
+ deviceId);
+ for (Group group : storedGroupEntries) {
+ log.trace("Stored Group {} for device {}", group, deviceId);
+ }
+ }
+
+ for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
+ Group group = it2.next();
+ if (storedGroupEntries.remove(group)) {
+ // we both have the group, let's update some info then.
+ log.trace("Group AUDIT: group {} exists "
+ + "in both planes for device {}",
+ group.id(), deviceId);
+ groupAdded(networkId, group);
+ it2.remove();
+ }
+ }
+ for (Group group : southboundGroupEntries) {
+ if (getGroup(networkId, group.deviceId(), group.id()) != null) {
+ // There is a group existing with the same id
+ // It is possible that group update is
+ // in progress while we got a stale info from switch
+ if (!storedGroupEntries.remove(getGroup(
+ networkId, group.deviceId(), group.id()))) {
+ log.warn("Group AUDIT: Inconsistent state:"
+ + "Group exists in ID based table while "
+ + "not present in key based table");
+ }
+ } else {
+ // there are groups in the switch that aren't in the store
+ log.trace("Group AUDIT: extraneous group {} exists "
+ + "in data plane for device {}",
+ group.id(), deviceId);
+ extraneousStoredEntries.remove(group);
+ extraneousGroup(networkId, group);
+ }
+ }
+ for (Group group : storedGroupEntries) {
+ // there are groups in the store that aren't in the switch
+ log.trace("Group AUDIT: group {} missing "
+ + "in data plane for device {}",
+ group.id(), deviceId);
+ groupMissing(networkId, group);
+ }
+ for (Group group : extraneousStoredEntries) {
+ // there are groups in the extraneous store that
+ // aren't in the switch
+ log.trace("Group AUDIT: clearing extransoeus group {} "
+ + "from store for device {}",
+ group.id(), deviceId);
+ removeExtraneousGroupEntry(networkId, group);
+ }
+
+ if (!deviceInitialAuditStatus) {
+ log.debug("Group AUDIT: Setting device {} initial "
+ + "AUDIT completed", deviceId);
+ deviceInitialAuditCompleted(networkId, deviceId, true);
+ }
+ }
+
+ @Override
+ public void notifyOfFailovers(NetworkId networkId, Collection<Group> failoverGroups) {
+ List<GroupEvent> failoverEvents = new ArrayList<>();
+ failoverGroups.forEach(group -> {
+ if (group.type() == Group.Type.FAILOVER) {
+ failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
+ }
+ });
+ notifyDelegate(networkId, failoverEvents);
+ }
+
+ private void groupMissing(NetworkId networkId, Group group) {
+ switch (group.state()) {
+ case PENDING_DELETE:
+ log.debug("Group {} delete confirmation from device {} " +
+ "of virtaual network {}",
+ group, group.deviceId(), networkId);
+ removeGroupEntry(networkId, group);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ case PENDING_UPDATE:
+ log.debug("Group {} is in store but not on device {}",
+ group, group.deviceId());
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+
+ existing = groupEntriesById.get(networkId)
+ .get(group.deviceId()).get(group.id());
+ }
+ if (existing == null) {
+ break;
+ }
+
+ log.trace("groupMissing: group "
+ + "entry {} in device {} moving "
+ + "from {} to PENDING_ADD",
+ existing.id(),
+ existing.deviceId(),
+ existing.state());
+ existing.setState(Group.GroupState.PENDING_ADD);
+ notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
+ group));
+ break;
+ default:
+ log.debug("Virtual network {} : Group {} has not been installed.",
+ networkId, group);
+ break;
+ }
+ }
+
+ private void extraneousGroup(NetworkId networkId, Group group) {
+ log.debug("Group {} is on device {} of virtual network{}, but not in store.",
+ group, group.deviceId(), networkId);
+ addOrUpdateExtraneousGroupEntry(networkId, group);
+ }
+
+ private void groupAdded(NetworkId networkId, Group group) {
+ log.trace("Group {} Added or Updated in device {} of virtual network {}",
+ group, group.deviceId(), networkId);
+ addOrUpdateGroupEntry(networkId, group);
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualIntentStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualIntentStore.java
new file mode 100644
index 0000000..15b6bcd
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualIntentStore.java
@@ -0,0 +1,268 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+import org.onosproject.store.Timestamp;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.PURGE_REQ;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Simple single-instance implementation of the intent store for virtual networks.
+ */
+
+@Component(immediate = true, service = VirtualNetworkIntentStore.class)
+public class SimpleVirtualIntentStore
+ extends AbstractVirtualStore<IntentEvent, IntentStoreDelegate>
+ implements VirtualNetworkIntentStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private final Map<NetworkId, Map<Key, IntentData>> currentByNetwork =
+ Maps.newConcurrentMap();
+ private final Map<NetworkId, Map<Key, IntentData>> pendingByNetwork =
+ Maps.newConcurrentMap();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+
+ @Override
+ public long getIntentCount(NetworkId networkId) {
+ return getCurrentMap(networkId).size();
+ }
+
+ @Override
+ public Iterable<Intent> getIntents(NetworkId networkId) {
+ return getCurrentMap(networkId).values().stream()
+ .map(IntentData::intent)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Iterable<IntentData> getIntentData(NetworkId networkId,
+ boolean localOnly, long olderThan) {
+ if (localOnly || olderThan > 0) {
+ long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
+ final SystemClockTimestamp time = new SystemClockTimestamp(older);
+ return getCurrentMap(networkId).values().stream()
+ .filter(data -> data.version().isOlderThan(time) &&
+ (!localOnly || isMaster(networkId, data.key())))
+ .collect(Collectors.toList());
+ }
+ return Lists.newArrayList(getCurrentMap(networkId).values());
+ }
+
+ @Override
+ public IntentState getIntentState(NetworkId networkId, Key intentKey) {
+ IntentData data = getCurrentMap(networkId).get(intentKey);
+ return (data != null) ? data.state() : null;
+ }
+
+ @Override
+ public List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey) {
+ IntentData data = getCurrentMap(networkId).get(intentKey);
+ if (data != null) {
+ return data.installables();
+ }
+ return null;
+ }
+
+ @Override
+ public void write(NetworkId networkId, IntentData newData) {
+ checkNotNull(newData);
+
+ synchronized (this) {
+ // TODO this could be refactored/cleaned up
+ IntentData currentData = getCurrentMap(networkId).get(newData.key());
+ IntentData pendingData = getPendingMap(networkId).get(newData.key());
+
+ if (IntentData.isUpdateAcceptable(currentData, newData)) {
+ if (pendingData != null) {
+ if (pendingData.state() == PURGE_REQ) {
+ getCurrentMap(networkId).remove(newData.key(), newData);
+ } else {
+ getCurrentMap(networkId).put(newData.key(), IntentData.copy(newData));
+ }
+
+ if (pendingData.version().compareTo(newData.version()) <= 0) {
+ // pendingData version is less than or equal to newData's
+ // Note: a new update for this key could be pending (it's version will be greater)
+ getPendingMap(networkId).remove(newData.key());
+ }
+ }
+ IntentEvent.getEvent(newData).ifPresent(e -> notifyDelegate(networkId, e));
+ }
+ }
+ }
+
+ @Override
+ public void batchWrite(NetworkId networkId, Iterable<IntentData> updates) {
+ for (IntentData data : updates) {
+ write(networkId, data);
+ }
+ }
+
+ @Override
+ public Intent getIntent(NetworkId networkId, Key key) {
+ IntentData data = getCurrentMap(networkId).get(key);
+ return (data != null) ? data.intent() : null;
+ }
+
+ @Override
+ public IntentData getIntentData(NetworkId networkId, Key key) {
+ IntentData currentData = getCurrentMap(networkId).get(key);
+ if (currentData == null) {
+ return null;
+ }
+ return IntentData.copy(currentData);
+ }
+
+ @Override
+ public void addPending(NetworkId networkId, IntentData data) {
+ if (data.version() == null) { // recompiled intents will already have a version
+ data = new IntentData(data.intent(), data.state(), new SystemClockTimestamp());
+ }
+ synchronized (this) {
+ IntentData existingData = getPendingMap(networkId).get(data.key());
+ if (existingData == null ||
+ // existing version is strictly less than data's version
+ // Note: if they are equal, we already have the update
+ // TODO maybe we should still make this <= to be safe?
+ existingData.version().compareTo(data.version()) < 0) {
+ getPendingMap(networkId).put(data.key(), data);
+
+ checkNotNull(delegateMap.get(networkId), "Store delegate is not set")
+ .process(IntentData.copy(data));
+ IntentEvent.getEvent(data).ifPresent(e -> notifyDelegate(networkId, e));
+ } else {
+ log.debug("IntentData {} is older than existing: {}",
+ data, existingData);
+ }
+ //TODO consider also checking the current map at this point
+ }
+ }
+
+ @Override
+ public boolean isMaster(NetworkId networkId, Key intentKey) {
+ return true;
+ }
+
+ @Override
+ public Iterable<Intent> getPending(NetworkId networkId) {
+ return getPendingMap(networkId).values().stream()
+ .map(IntentData::intent)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Iterable<IntentData> getPendingData(NetworkId networkId) {
+ return Lists.newArrayList(getPendingMap(networkId).values());
+ }
+
+ @Override
+ public IntentData getPendingData(NetworkId networkId, Key intentKey) {
+ return getPendingMap(networkId).get(intentKey);
+ }
+
+ @Override
+ public Iterable<IntentData> getPendingData(NetworkId networkId,
+ boolean localOnly, long olderThan) {
+ long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
+ final SystemClockTimestamp time = new SystemClockTimestamp(older);
+ return getPendingMap(networkId).values().stream()
+ .filter(data -> data.version().isOlderThan(time) &&
+ (!localOnly || isMaster(networkId, data.key())))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the current intent map for a specific virtual network.
+ *
+ * @param networkId a virtual network identifier
+ * @return the current map for the requested virtual network
+ */
+ private Map<Key, IntentData> getCurrentMap(NetworkId networkId) {
+ currentByNetwork.computeIfAbsent(networkId,
+ n -> Maps.newConcurrentMap());
+ return currentByNetwork.get(networkId);
+ }
+
+ /**
+ * Returns the pending intent map for a specific virtual network.
+ *
+ * @param networkId a virtual network identifier
+ * @return the pending intent map for the requested virtual network
+ */
+ private Map<Key, IntentData> getPendingMap(NetworkId networkId) {
+ pendingByNetwork.computeIfAbsent(networkId,
+ n -> Maps.newConcurrentMap());
+ return pendingByNetwork.get(networkId);
+ }
+
+ public class SystemClockTimestamp implements Timestamp {
+
+ private final long nanoTimestamp;
+
+ public SystemClockTimestamp() {
+ nanoTimestamp = System.nanoTime();
+ }
+
+ public SystemClockTimestamp(long timestamp) {
+ nanoTimestamp = timestamp;
+ }
+
+ @Override
+ public int compareTo(Timestamp o) {
+ checkArgument(o instanceof SystemClockTimestamp,
+ "Must be SystemClockTimestamp", o);
+ SystemClockTimestamp that = (SystemClockTimestamp) o;
+
+ return ComparisonChain.start()
+ .compare(this.nanoTimestamp, that.nanoTimestamp)
+ .result();
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStore.java
new file mode 100644
index 0000000..68d8ed8
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStore.java
@@ -0,0 +1,538 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterEventListener;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.Node;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.core.Version;
+import org.onosproject.core.VersionService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMastershipStore;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipInfo;
+import org.onosproject.mastership.MastershipStoreDelegate;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.onosproject.mastership.MastershipEvent.Type.BACKUPS_CHANGED;
+import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the virtual network mastership store to manage inventory of
+ * mastership using trivial in-memory implementation.
+ */
+@Component(immediate = true, service = VirtualNetworkMastershipStore.class)
+public class SimpleVirtualMastershipStore
+ extends AbstractVirtualStore<MastershipEvent, MastershipStoreDelegate>
+ implements VirtualNetworkMastershipStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final int NOTHING = 0;
+ private static final int INIT = 1;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected VersionService versionService;
+
+ //devices mapped to their masters, to emulate multiple nodes
+ protected final Map<NetworkId, Map<DeviceId, NodeId>> masterMapByNetwork =
+ new HashMap<>();
+ //emulate backups with pile of nodes
+ protected final Map<NetworkId, Map<DeviceId, List<NodeId>>> backupsByNetwork =
+ new HashMap<>();
+ //terms
+ protected final Map<NetworkId, Map<DeviceId, AtomicInteger>> termMapByNetwork =
+ new HashMap<>();
+
+ @Activate
+ public void activate() {
+ if (clusterService == null) {
+ clusterService = createFakeClusterService();
+ }
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public CompletableFuture<MastershipRole> requestRole(NetworkId networkId,
+ DeviceId deviceId) {
+ //query+possible reelection
+ NodeId node = clusterService.getLocalNode().id();
+ MastershipRole role = getRole(networkId, node, deviceId);
+
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+
+ switch (role) {
+ case MASTER:
+ return CompletableFuture.completedFuture(MastershipRole.MASTER);
+ case STANDBY:
+ if (getMaster(networkId, deviceId) == null) {
+ // no master => become master
+ masterMap.put(deviceId, node);
+ incrementTerm(networkId, deviceId);
+ // remove from backup list
+ removeFromBackups(networkId, deviceId, node);
+ notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ return CompletableFuture.completedFuture(MastershipRole.MASTER);
+ }
+ return CompletableFuture.completedFuture(MastershipRole.STANDBY);
+ case NONE:
+ if (getMaster(networkId, deviceId) == null) {
+ // no master => become master
+ masterMap.put(deviceId, node);
+ incrementTerm(networkId, deviceId);
+ notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ return CompletableFuture.completedFuture(MastershipRole.MASTER);
+ }
+ // add to backup list
+ if (addToBackup(networkId, deviceId, node)) {
+ notifyDelegate(networkId, new MastershipEvent(BACKUPS_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ }
+ return CompletableFuture.completedFuture(MastershipRole.STANDBY);
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ }
+ return CompletableFuture.completedFuture(role);
+ }
+
+ @Override
+ public MastershipRole getRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ //just query
+ NodeId current = masterMap.get(deviceId);
+ MastershipRole role;
+
+ if (current != null && current.equals(nodeId)) {
+ return MastershipRole.MASTER;
+ }
+
+ if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
+ role = MastershipRole.STANDBY;
+ } else {
+ role = MastershipRole.NONE;
+ }
+ return role;
+ }
+
+ @Override
+ public NodeId getMaster(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ return masterMap.get(deviceId);
+ }
+
+ @Override
+ public RoleInfo getNodes(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ return new RoleInfo(masterMap.get(deviceId),
+ backups.getOrDefault(deviceId, ImmutableList.of()));
+ }
+
+ @Override
+ public MastershipInfo getMastership(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+ ImmutableMap.Builder<NodeId, MastershipRole> roleBuilder = ImmutableMap.builder();
+ NodeId master = masterMap.get(deviceId);
+ if (master != null) {
+ roleBuilder.put(master, MastershipRole.MASTER);
+ }
+ backups.getOrDefault(deviceId, Collections.emptyList())
+ .forEach(nodeId -> roleBuilder.put(nodeId, MastershipRole.STANDBY));
+ clusterService.getNodes().stream()
+ .filter(node -> !masterMap.containsValue(node.id()))
+ .filter(node -> !backups.get(deviceId).contains(node.id()))
+ .forEach(node -> roleBuilder.put(node.id(), MastershipRole.NONE));
+ return new MastershipInfo(
+ termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING)).get(),
+ Optional.ofNullable(master),
+ roleBuilder.build());
+ }
+
+ @Override
+ public Set<DeviceId> getDevices(NetworkId networkId, NodeId nodeId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+
+ Set<DeviceId> ids = new HashSet<>();
+ for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
+ if (Objects.equals(d.getValue(), nodeId)) {
+ ids.add(d.getKey());
+ }
+ }
+ return ids;
+ }
+
+ @Override
+ public synchronized CompletableFuture<MastershipEvent> setMaster(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+
+ MastershipRole role = getRole(networkId, nodeId, deviceId);
+ switch (role) {
+ case MASTER:
+ // no-op
+ return CompletableFuture.completedFuture(null);
+ case STANDBY:
+ case NONE:
+ NodeId prevMaster = masterMap.put(deviceId, nodeId);
+ incrementTerm(networkId, deviceId);
+ removeFromBackups(networkId, deviceId, nodeId);
+ addToBackup(networkId, deviceId, prevMaster);
+ break;
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ return null;
+ }
+
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(MASTER_CHANGED, deviceId, getMastership(networkId, deviceId)));
+ }
+
+ @Override
+ public MastershipTerm getTermFor(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
+
+ if ((termMap.get(deviceId) == null)) {
+ return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
+ }
+ return MastershipTerm.of(
+ masterMap.get(deviceId), termMap.get(deviceId).get());
+ }
+
+ @Override
+ public CompletableFuture<MastershipEvent> setStandby(NetworkId networkId,
+ NodeId nodeId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+
+ MastershipRole role = getRole(networkId, nodeId, deviceId);
+ switch (role) {
+ case MASTER:
+ NodeId backup = reelect(networkId, deviceId, nodeId);
+ if (backup == null) {
+ // no master alternative
+ masterMap.remove(deviceId);
+ // TODO: Should there be new event type for no MASTER?
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(MASTER_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ } else {
+ NodeId prevMaster = masterMap.put(deviceId, backup);
+ incrementTerm(networkId, deviceId);
+ addToBackup(networkId, deviceId, prevMaster);
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(MASTER_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ }
+
+ case STANDBY:
+ case NONE:
+ boolean modified = addToBackup(networkId, deviceId, nodeId);
+ if (modified) {
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(BACKUPS_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ }
+ break;
+
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ }
+ return null;
+ }
+
+
+ /**
+ * Dumbly selects next-available node that's not the current one.
+ * emulate leader election.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId a virtual device identifier
+ * @param nodeId a nod identifier
+ * @return Next available node as a leader
+ */
+ private synchronized NodeId reelect(NetworkId networkId, DeviceId deviceId,
+ NodeId nodeId) {
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ List<NodeId> stbys = backups.getOrDefault(deviceId, Collections.emptyList());
+ NodeId backup = null;
+ for (NodeId n : stbys) {
+ if (!n.equals(nodeId)) {
+ backup = n;
+ break;
+ }
+ }
+ stbys.remove(backup);
+ return backup;
+ }
+
+ @Override
+ public synchronized CompletableFuture<MastershipEvent>
+ relinquishRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+
+ MastershipRole role = getRole(networkId, nodeId, deviceId);
+ switch (role) {
+ case MASTER:
+ NodeId backup = reelect(networkId, deviceId, nodeId);
+ masterMap.put(deviceId, backup);
+ incrementTerm(networkId, deviceId);
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(MASTER_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+
+ case STANDBY:
+ if (removeFromBackups(networkId, deviceId, nodeId)) {
+ return CompletableFuture.completedFuture(
+ new MastershipEvent(BACKUPS_CHANGED, deviceId,
+ getMastership(networkId, deviceId)));
+ }
+ break;
+
+ case NONE:
+ break;
+
+ default:
+ log.warn("unknown Mastership Role {}", role);
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public void relinquishAllRole(NetworkId networkId, NodeId nodeId) {
+ Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ List<CompletableFuture<MastershipEvent>> eventFutures = new ArrayList<>();
+ Set<DeviceId> toRelinquish = new HashSet<>();
+
+ masterMap.entrySet().stream()
+ .filter(entry -> nodeId.equals(entry.getValue()))
+ .forEach(entry -> toRelinquish.add(entry.getKey()));
+
+ backups.entrySet().stream()
+ .filter(entry -> entry.getValue().contains(nodeId))
+ .forEach(entry -> toRelinquish.add(entry.getKey()));
+
+ toRelinquish.forEach(deviceId -> eventFutures.add(
+ relinquishRole(networkId, nodeId, deviceId)));
+
+ eventFutures.forEach(future -> {
+ future.whenComplete((event, error) -> notifyDelegate(networkId, event));
+ });
+ }
+
+ /**
+ * Increase the term for a device, and store it.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId a virtual device identifier
+ */
+ private synchronized void incrementTerm(NetworkId networkId, DeviceId deviceId) {
+ Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
+
+ AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
+ term.incrementAndGet();
+ termMap.put(deviceId, term);
+ }
+
+ /**
+ * Remove backup node for a device.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId a virtual device identifier
+ * @param nodeId a node identifier
+ * @return True if success
+ */
+ private synchronized boolean removeFromBackups(NetworkId networkId,
+ DeviceId deviceId, NodeId nodeId) {
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
+ boolean modified = stbys.remove(nodeId);
+ backups.put(deviceId, stbys);
+ return modified;
+ }
+
+ /**
+ * add to backup if not there already, silently ignores null node.
+ *
+ * @param networkId a virtual network identifier
+ * @param deviceId a virtual device identifier
+ * @param nodeId a node identifier
+ * @return True if success
+ */
+ private synchronized boolean addToBackup(NetworkId networkId,
+ DeviceId deviceId, NodeId nodeId) {
+ Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
+
+ boolean modified = false;
+ List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
+ if (nodeId != null && !stbys.contains(nodeId)) {
+ stbys.add(nodeId);
+ backups.put(deviceId, stbys);
+ modified = true;
+ }
+ return modified;
+ }
+
+ /**
+ * Returns deviceId-master map for a specified virtual network.
+ *
+ * @param networkId a virtual network identifier
+ * @return DeviceId-master map of a given virtual network.
+ */
+ private Map<DeviceId, NodeId> getMasterMap(NetworkId networkId) {
+ return masterMapByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
+ }
+
+ /**
+ * Returns deviceId-backups map for a specified virtual network.
+ *
+ * @param networkId a virtual network identifier
+ * @return DeviceId-backups map of a given virtual network.
+ */
+ private Map<DeviceId, List<NodeId>> getBackups(NetworkId networkId) {
+ return backupsByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
+ }
+
+ /**
+ * Returns deviceId-terms map for a specified virtual network.
+ *
+ * @param networkId a virtual network identifier
+ * @return DeviceId-terms map of a given virtual network.
+ */
+ private Map<DeviceId, AtomicInteger> getTermMap(NetworkId networkId) {
+ return termMapByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
+ }
+
+ /**
+ * Returns a fake cluster service for a test purpose only.
+ *
+ * @return a fake cluster service
+ */
+ private ClusterService createFakeClusterService() {
+ // just for ease of unit test
+ final ControllerNode instance =
+ new DefaultControllerNode(new NodeId("local"),
+ IpAddress.valueOf("127.0.0.1"));
+
+ ClusterService faceClusterService = new ClusterService() {
+
+ private final Instant creationTime = Instant.now();
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return instance;
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ return ImmutableSet.of(instance);
+ }
+
+ @Override
+ public Set<Node> getConsensusNodes() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ if (instance.id().equals(nodeId)) {
+ return instance;
+ }
+ return null;
+ }
+
+ @Override
+ public ControllerNode.State getState(NodeId nodeId) {
+ if (instance.id().equals(nodeId)) {
+ return ControllerNode.State.ACTIVE;
+ } else {
+ return ControllerNode.State.INACTIVE;
+ }
+ }
+
+ @Override
+ public Version getVersion(NodeId nodeId) {
+ if (instance.id().equals(nodeId)) {
+ return versionService.version();
+ }
+ return null;
+ }
+
+ @Override
+ public Instant getLastUpdatedInstant(NodeId nodeId) {
+ return creationTime;
+ }
+
+ @Override
+ public void addListener(ClusterEventListener listener) {
+ }
+
+ @Override
+ public void removeListener(ClusterEventListener listener) {
+ }
+ };
+ return faceClusterService;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMeterStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMeterStore.java
new file mode 100644
index 0000000..5add89d
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMeterStore.java
@@ -0,0 +1,265 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMeterStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterEvent;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterFeatures;
+import org.onosproject.net.meter.MeterFeaturesKey;
+import org.onosproject.net.meter.MeterKey;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterStoreDelegate;
+import org.onosproject.net.meter.MeterStoreResult;
+import org.onosproject.store.service.StorageException;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.onosproject.net.meter.MeterFailReason.TIMEOUT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the virtual meter store for a single instance.
+ */
+//TODO: support distributed meter store for virtual networks
+@Component(immediate = true, service = VirtualNetworkMeterStore.class)
+public class SimpleVirtualMeterStore
+ extends AbstractVirtualStore<MeterEvent, MeterStoreDelegate>
+ implements VirtualNetworkMeterStore {
+
+ private Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ private ConcurrentMap<NetworkId, ConcurrentMap<MeterKey, MeterData>> meterMap =
+ Maps.newConcurrentMap();
+
+ private NodeId local;
+
+ private ConcurrentMap<NetworkId, ConcurrentMap<MeterFeaturesKey, MeterFeatures>>
+ meterFeatureMap = Maps.newConcurrentMap();
+
+ private ConcurrentMap<NetworkId,
+ ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>>> futuresMap =
+ Maps.newConcurrentMap();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ local = clusterService.getLocalNode().id();
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ private ConcurrentMap<MeterKey, MeterData> getMetersByNetwork(NetworkId networkId) {
+ meterMap.computeIfAbsent(networkId, m -> new ConcurrentHashMap<>());
+ return meterMap.get(networkId);
+ }
+
+ private ConcurrentMap<MeterFeaturesKey, MeterFeatures>
+ getMeterFeaturesByNetwork(NetworkId networkId) {
+ meterFeatureMap.computeIfAbsent(networkId, f -> new ConcurrentHashMap<>());
+ return meterFeatureMap.get(networkId);
+ }
+
+ private ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>>
+ getFuturesByNetwork(NetworkId networkId) {
+ futuresMap.computeIfAbsent(networkId, f -> new ConcurrentHashMap<>());
+ return futuresMap.get(networkId);
+ }
+
+ @Override
+ public CompletableFuture<MeterStoreResult> storeMeter(NetworkId networkId, Meter meter) {
+
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>> futures =
+ getFuturesByNetwork(networkId);
+
+ CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
+ MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+ futures.put(key, future);
+ MeterData data = new MeterData(meter, null, local);
+
+ try {
+ meters.put(key, data);
+ } catch (StorageException e) {
+ future.completeExceptionally(e);
+ }
+
+ return future;
+ }
+
+ @Override
+ public CompletableFuture<MeterStoreResult> deleteMeter(NetworkId networkId, Meter meter) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>> futures =
+ getFuturesByNetwork(networkId);
+
+ CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
+ MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+ futures.put(key, future);
+
+ MeterData data = new MeterData(meter, null, local);
+
+ // update the state of the meter. It will be pruned by observing
+ // that it has been removed from the dataplane.
+ try {
+ if (meters.computeIfPresent(key, (k, v) -> data) == null) {
+ future.complete(MeterStoreResult.success());
+ }
+ } catch (StorageException e) {
+ future.completeExceptionally(e);
+ }
+
+ return future;
+ }
+
+ @Override
+ public MeterStoreResult storeMeterFeatures(NetworkId networkId, MeterFeatures meterfeatures) {
+ ConcurrentMap<MeterFeaturesKey, MeterFeatures> meterFeatures
+ = getMeterFeaturesByNetwork(networkId);
+
+ MeterStoreResult result = MeterStoreResult.success();
+ MeterFeaturesKey key = MeterFeaturesKey.key(meterfeatures.deviceId());
+ try {
+ meterFeatures.putIfAbsent(key, meterfeatures);
+ } catch (StorageException e) {
+ result = MeterStoreResult.fail(TIMEOUT);
+ }
+ return result;
+ }
+
+ @Override
+ public MeterStoreResult deleteMeterFeatures(NetworkId networkId, DeviceId deviceId) {
+ ConcurrentMap<MeterFeaturesKey, MeterFeatures> meterFeatures
+ = getMeterFeaturesByNetwork(networkId);
+
+ MeterStoreResult result = MeterStoreResult.success();
+ MeterFeaturesKey key = MeterFeaturesKey.key(deviceId);
+ try {
+ meterFeatures.remove(key);
+ } catch (StorageException e) {
+ result = MeterStoreResult.fail(TIMEOUT);
+ }
+ return result;
+ }
+
+ @Override
+ public CompletableFuture<MeterStoreResult> updateMeter(NetworkId networkId, Meter meter) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+ ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>> futures =
+ getFuturesByNetwork(networkId);
+
+ CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
+ MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+ futures.put(key, future);
+
+ MeterData data = new MeterData(meter, null, local);
+ try {
+ if (meters.computeIfPresent(key, (k, v) -> data) == null) {
+ future.complete(MeterStoreResult.fail(MeterFailReason.INVALID_METER));
+ }
+ } catch (StorageException e) {
+ future.completeExceptionally(e);
+ }
+ return future;
+ }
+
+ @Override
+ public void updateMeterState(NetworkId networkId, Meter meter) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+ meters.computeIfPresent(key, (k, v) -> {
+ DefaultMeter m = (DefaultMeter) v.meter();
+ m.setState(meter.state());
+ m.setProcessedPackets(meter.packetsSeen());
+ m.setProcessedBytes(meter.bytesSeen());
+ m.setLife(meter.life());
+ // TODO: Prune if drops to zero.
+ m.setReferenceCount(meter.referenceCount());
+ return new MeterData(m, null, v.origin());
+ });
+ }
+
+ @Override
+ public Meter getMeter(NetworkId networkId, MeterKey key) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ MeterData data = meters.get(key);
+ return data == null ? null : data.meter();
+ }
+
+ @Override
+ public Collection<Meter> getAllMeters(NetworkId networkId) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ return Collections2.transform(meters.values(), MeterData::meter);
+ }
+
+ @Override
+ public void failedMeter(NetworkId networkId, MeterOperation op, MeterFailReason reason) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+
+ MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().id());
+ meters.computeIfPresent(key, (k, v) ->
+ new MeterData(v.meter(), reason, v.origin()));
+ }
+
+ @Override
+ public void deleteMeterNow(NetworkId networkId, Meter m) {
+ ConcurrentMap<MeterKey, MeterData> meters = getMetersByNetwork(networkId);
+ ConcurrentMap<MeterKey, CompletableFuture<MeterStoreResult>> futures =
+ getFuturesByNetwork(networkId);
+
+ MeterKey key = MeterKey.key(m.deviceId(), m.id());
+ futures.remove(key);
+ meters.remove(key);
+ }
+
+ @Override
+ public long getMaxMeters(NetworkId networkId, MeterFeaturesKey key) {
+ ConcurrentMap<MeterFeaturesKey, MeterFeatures> meterFeatures
+ = getMeterFeaturesByNetwork(networkId);
+
+ MeterFeatures features = meterFeatures.get(key);
+ return features == null ? 0L : features.maxMeter();
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualPacketStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualPacketStore.java
new file mode 100644
index 0000000..e0bb7c6
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualPacketStore.java
@@ -0,0 +1,125 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketStoreDelegate;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Simple single instance implementation of the virtual packet store.
+ */
+//TODO: support distributed packet store for virtual networks
+
+@Component(immediate = true, service = VirtualNetworkPacketStore.class)
+public class SimpleVirtualPacketStore
+ extends AbstractVirtualStore<PacketEvent, PacketStoreDelegate>
+ implements VirtualNetworkPacketStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private Map<NetworkId, Map<TrafficSelector, Set<PacketRequest>>> requests
+ = Maps.newConcurrentMap();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public void emit(NetworkId networkId, OutboundPacket packet) {
+ notifyDelegate(networkId, new PacketEvent(PacketEvent.Type.EMIT, packet));
+ }
+
+ @Override
+ public void requestPackets(NetworkId networkId, PacketRequest request) {
+ requests.computeIfAbsent(networkId, k -> Maps.newConcurrentMap());
+
+ requests.get(networkId).compute(request.selector(), (s, existingRequests) -> {
+ if (existingRequests == null) {
+ if (hasDelegate(networkId)) {
+ delegateMap.get(networkId).requestPackets(request);
+ }
+ return ImmutableSet.of(request);
+ } else if (!existingRequests.contains(request)) {
+ if (hasDelegate(networkId)) {
+ delegateMap.get(networkId).requestPackets(request);
+ }
+ return ImmutableSet.<PacketRequest>builder()
+ .addAll(existingRequests)
+ .add(request)
+ .build();
+ } else {
+ return existingRequests;
+ }
+ });
+ }
+
+ @Override
+ public void cancelPackets(NetworkId networkId, PacketRequest request) {
+ requests.get(networkId).computeIfPresent(request.selector(), (s, existingRequests) -> {
+ if (existingRequests.contains(request)) {
+ HashSet<PacketRequest> newRequests = Sets.newHashSet(existingRequests);
+ newRequests.remove(request);
+ if (hasDelegate(networkId)) {
+ delegateMap.get(networkId).cancelPackets(request);
+ }
+ if (newRequests.size() > 0) {
+ return ImmutableSet.copyOf(newRequests);
+ } else {
+ return null;
+ }
+ } else {
+ return existingRequests;
+ }
+ });
+ }
+
+ @Override
+ public List<PacketRequest> existingRequests(NetworkId networkId) {
+ List<PacketRequest> list = Lists.newArrayList();
+ if (requests.get(networkId) != null) {
+ requests.get(networkId).values().forEach(list::addAll);
+ list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue());
+ }
+ return list;
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/package-info.java
new file mode 100644
index 0000000..99b5d82
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/**
+ * Implementation of virtual network stores.
+ */
+package org.onosproject.incubator.net.virtual.store.impl;
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualDeviceId.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualDeviceId.java
new file mode 100644
index 0000000..6a38661
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualDeviceId.java
@@ -0,0 +1,64 @@
+/*
+ * 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.store.impl.primitives;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to isolate device id from other virtual networks.
+ */
+public class VirtualDeviceId {
+
+ NetworkId networkId;
+ DeviceId deviceId;
+
+ public VirtualDeviceId(NetworkId networkId, DeviceId deviceId) {
+ this.networkId = networkId;
+ this.deviceId = deviceId;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, deviceId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof VirtualDeviceId) {
+ VirtualDeviceId that = (VirtualDeviceId) obj;
+ return this.deviceId.equals(that.deviceId) &&
+ this.networkId.equals(that.networkId);
+ }
+ return false;
+ }
+}
+
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowEntry.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowEntry.java
new file mode 100644
index 0000000..2a00883
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowEntry.java
@@ -0,0 +1,63 @@
+/*
+ * 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.store.impl.primitives;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.flow.FlowEntry;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to encapsulate flow entry.
+ */
+public class VirtualFlowEntry {
+ NetworkId networkId;
+ FlowEntry flowEntry;
+
+ public VirtualFlowEntry(NetworkId networkId, FlowEntry flowEntry) {
+ this.networkId = networkId;
+ this.flowEntry = flowEntry;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ public FlowEntry flowEntry() {
+ return flowEntry;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, flowEntry);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof VirtualFlowEntry) {
+ VirtualFlowEntry that = (VirtualFlowEntry) other;
+ return this.networkId.equals(that.networkId) &&
+ this.flowEntry.equals(that.flowEntry);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRule.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRule.java
new file mode 100644
index 0000000..ea64904
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.store.impl.primitives;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.flow.FlowRule;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to encapsulate flow rule.
+ */
+public class VirtualFlowRule {
+ NetworkId networkId;
+ FlowRule rule;
+
+ public VirtualFlowRule(NetworkId networkId, FlowRule rule) {
+ this.networkId = networkId;
+ this.rule = rule;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ public FlowRule rule() {
+ return rule;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, rule);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof VirtualFlowRule) {
+ VirtualFlowRule that = (VirtualFlowRule) other;
+ return this.networkId.equals(that.networkId) &&
+ this.rule.equals(that.rule);
+ } else {
+ return false;
+ }
+ }
+}
+
+
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchEvent.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchEvent.java
new file mode 100644
index 0000000..d1b5d57
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchEvent.java
@@ -0,0 +1,65 @@
+/*
+ * 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.store.impl.primitives;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to encapsulate flow rule batch event.
+ */
+public class VirtualFlowRuleBatchEvent {
+ NetworkId networkId;
+ FlowRuleBatchEvent event;
+
+ public VirtualFlowRuleBatchEvent(NetworkId networkId, FlowRuleBatchEvent event) {
+ this.networkId = networkId;
+ this.event = event;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ public FlowRuleBatchEvent event() {
+ return event;
+ }
+
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, event);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof VirtualFlowRuleBatchEvent) {
+ VirtualFlowRuleBatchEvent that = (VirtualFlowRuleBatchEvent) other;
+ return this.networkId.equals(that.networkId) &&
+ this.event.equals(that.event);
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchOperation.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchOperation.java
new file mode 100644
index 0000000..8db1801
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/VirtualFlowRuleBatchOperation.java
@@ -0,0 +1,64 @@
+/*
+ * 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.store.impl.primitives;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to encapsulate flow rule batch operation.
+ */
+public class VirtualFlowRuleBatchOperation {
+ NetworkId networkId;
+ FlowRuleBatchOperation operation;
+
+ public VirtualFlowRuleBatchOperation(NetworkId networkId,
+ FlowRuleBatchOperation operation) {
+ this.networkId = networkId;
+ this.operation = operation;
+ }
+
+ public NetworkId networkId() {
+ return networkId;
+ }
+
+ public FlowRuleBatchOperation operation() {
+ return operation;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkId, operation);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof VirtualFlowRuleBatchOperation) {
+ VirtualFlowRuleBatchOperation that = (VirtualFlowRuleBatchOperation) other;
+ return this.networkId.equals(that.networkId) &&
+ this.operation.equals(that.operation);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/package-info.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/package-info.java
new file mode 100644
index 0000000..ee0de54
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/primitives/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation of distributed virtual network store primitives.
+ */
+package org.onosproject.incubator.net.virtual.store.impl.primitives;
diff --git a/apps/virtual/app/src/main/resources/definitions/TenantId.json b/apps/virtual/app/src/main/resources/definitions/TenantId.json
new file mode 100644
index 0000000..237a9c4
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/TenantId.json
@@ -0,0 +1,14 @@
+{
+ "type": "object",
+ "title": "TenantId",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "String",
+ "description": "Tenant identifier",
+ "example": "Tenant123"
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/TenantIds.json b/apps/virtual/app/src/main/resources/definitions/TenantIds.json
new file mode 100644
index 0000000..f568b9f
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/TenantIds.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "title": "tenants",
+ "required": [
+ "tenants"
+ ],
+ "properties": {
+ "tenants": {
+ "type": "array",
+ "xml": {
+ "name": "tenants",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "tenant",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "String",
+ "description": "Tenant identifier",
+ "example": "Tenant123"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualDevice.json b/apps/virtual/app/src/main/resources/definitions/VirtualDevice.json
new file mode 100644
index 0000000..ada054c
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualDevice.json
@@ -0,0 +1,20 @@
+{
+ "type": "object",
+ "title": "vdev",
+ "required": [
+ "networkId",
+ "deviceId"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "deviceId": {
+ "type": "String",
+ "description": "Device identifier",
+ "example": "of:0000000000000042"
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualDevices.json b/apps/virtual/app/src/main/resources/definitions/VirtualDevices.json
new file mode 100644
index 0000000..61e4071
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualDevices.json
@@ -0,0 +1,36 @@
+{
+ "type": "object",
+ "title": "VirtualDevices",
+ "required": [
+ "devices"
+ ],
+ "properties": {
+ "devices": {
+ "type": "array",
+ "xml": {
+ "name": "devices",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "vdev",
+ "required": [
+ "networkId",
+ "deviceId"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "deviceId": {
+ "type": "String",
+ "description": "Device identifier",
+ "example": "of:0000000000000042"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualHost.json b/apps/virtual/app/src/main/resources/definitions/VirtualHost.json
new file mode 100644
index 0000000..4035694
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualHost.json
@@ -0,0 +1,63 @@
+{
+ "type": "object",
+ "title": "host",
+ "required": [
+ "networkId",
+ "id",
+ "mac",
+ "vlan",
+ "ipAddresses",
+ "location"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "id": {
+ "type": "string",
+ "example": "46:E4:3C:A4:17:C8/-1"
+ },
+ "mac": {
+ "type": "string",
+ "example": "46:E4:3C:A4:17:C8"
+ },
+ "vlan": {
+ "type": "string",
+ "example": "-1"
+ },
+ "ipAddresses": {
+ "type": "array",
+ "xml": {
+ "name": "hosts",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string",
+ "example": "127.0.0.1"
+ }
+ },
+ "locations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "title": "location",
+ "required": [
+ "elementId",
+ "port"
+ ],
+ "properties": {
+ "elementId": {
+ "type": "string",
+ "example": "of:0000000000000002"
+ },
+ "port": {
+ "type": "string",
+ "example": "3"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualHostPut.json b/apps/virtual/app/src/main/resources/definitions/VirtualHostPut.json
new file mode 100644
index 0000000..3026478
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualHostPut.json
@@ -0,0 +1,58 @@
+{
+ "type": "object",
+ "title": "host",
+ "required": [
+ "networkId",
+ "mac",
+ "vlan",
+ "ipAddresses",
+ "location"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "mac": {
+ "type": "string",
+ "example": "46:E4:3C:A4:17:C8"
+ },
+ "vlan": {
+ "type": "string",
+ "example": "-1"
+ },
+ "ipAddresses": {
+ "type": "array",
+ "xml": {
+ "name": "hosts",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string",
+ "example": "127.0.0.1"
+ }
+ },
+ "locations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "title": "location",
+ "required": [
+ "elementId",
+ "port"
+ ],
+ "properties": {
+ "elementId": {
+ "type": "string",
+ "example": "of:0000000000000002"
+ },
+ "port": {
+ "type": "string",
+ "example": "3"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualHosts.json b/apps/virtual/app/src/main/resources/definitions/VirtualHosts.json
new file mode 100644
index 0000000..979a3f7
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualHosts.json
@@ -0,0 +1,79 @@
+{
+ "type": "object",
+ "title": "hosts",
+ "required": [
+ "hosts"
+ ],
+ "properties": {
+ "hosts": {
+ "type": "array",
+ "xml": {
+ "name": "hosts",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "host",
+ "required": [
+ "networkId",
+ "id",
+ "mac",
+ "vlan",
+ "ipAddresses",
+ "location"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "id": {
+ "type": "string",
+ "example": "46:E4:3C:A4:17:C8/-1"
+ },
+ "mac": {
+ "type": "string",
+ "example": "46:E4:3C:A4:17:C8"
+ },
+ "vlan": {
+ "type": "string",
+ "example": "-1"
+ },
+ "ipAddresses": {
+ "type": "array",
+ "xml": {
+ "name": "hosts",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string",
+ "example": "127.0.0.1"
+ }
+ },
+ "locations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "title": "location",
+ "required": [
+ "elementId",
+ "port"
+ ],
+ "properties": {
+ "elementId": {
+ "type": "string",
+ "example": "of:0000000000000002"
+ },
+ "port": {
+ "type": "string",
+ "example": "3"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualLink.json b/apps/virtual/app/src/main/resources/definitions/VirtualLink.json
new file mode 100644
index 0000000..8550eb4
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualLink.json
@@ -0,0 +1,62 @@
+{
+ "type": "object",
+ "title": "vlink",
+ "required": [
+ "networkId",
+ "src",
+ "dst",
+ "type",
+ "state"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "src": {
+ "type": "object",
+ "title": "src",
+ "required": [
+ "port",
+ "device"
+ ],
+ "properties": {
+ "port": {
+ "type": "int64",
+ "example": 3
+ },
+ "device": {
+ "type": "string",
+ "example": "of:0000000000000002"
+ }
+ }
+ },
+ "dst": {
+ "type": "object",
+ "title": "dst",
+ "required": [
+ "port",
+ "device"
+ ],
+ "properties": {
+ "port": {
+ "type": "int64",
+ "example": 2
+ },
+ "device": {
+ "type": "string",
+ "example": "of:0000000000000003"
+ }
+ }
+ },
+ "type": {
+ "type": "string",
+ "example": "DIRECT"
+ },
+ "state": {
+ "type": "string",
+ "example": "ACTIVE"
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualLinks.json b/apps/virtual/app/src/main/resources/definitions/VirtualLinks.json
new file mode 100644
index 0000000..8163356
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualLinks.json
@@ -0,0 +1,78 @@
+{
+ "type": "object",
+ "title": "VirtualLinks",
+ "required": [
+ "links"
+ ],
+ "properties": {
+ "links": {
+ "type": "array",
+ "xml": {
+ "name": "links",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "vlink",
+ "required": [
+ "networkId",
+ "src",
+ "dst",
+ "type",
+ "state"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "src": {
+ "type": "object",
+ "title": "src",
+ "required": [
+ "port",
+ "device"
+ ],
+ "properties": {
+ "port": {
+ "type": "int64",
+ "example": 3
+ },
+ "device": {
+ "type": "string",
+ "example": "of:0000000000000002"
+ }
+ }
+ },
+ "dst": {
+ "type": "object",
+ "title": "dst",
+ "required": [
+ "port",
+ "device"
+ ],
+ "properties": {
+ "port": {
+ "type": "int64",
+ "example": 2
+ },
+ "device": {
+ "type": "string",
+ "example": "of:0000000000000003"
+ }
+ }
+ },
+ "type": {
+ "type": "string",
+ "example": "VIRTUAL"
+ },
+ "state": {
+ "type": "string",
+ "example": "ACTIVE"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualNetworks.json b/apps/virtual/app/src/main/resources/definitions/VirtualNetworks.json
new file mode 100644
index 0000000..6ab1bde
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualNetworks.json
@@ -0,0 +1,36 @@
+{
+ "type": "object",
+ "title": "VirtualNetworks",
+ "required": [
+ "vnets"
+ ],
+ "properties": {
+ "vnets": {
+ "type": "array",
+ "xml": {
+ "name": "vnets",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "vnet",
+ "required": [
+ "networkId",
+ "tenantId"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "tenantId": {
+ "type": "String",
+ "description": "Tenant identifier",
+ "example": "Tenant123"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualPort.json b/apps/virtual/app/src/main/resources/definitions/VirtualPort.json
new file mode 100644
index 0000000..d1b8e47
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualPort.json
@@ -0,0 +1,38 @@
+{
+ "type": "object",
+ "title": "vport",
+ "required": [
+ "networkId",
+ "deviceId",
+ "portNum",
+ "physDeviceId",
+ "physPortNum"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "deviceId": {
+ "type": "String",
+ "description": "Virtual device identifier",
+ "example": "of:0000000000000042"
+ },
+ "portNum": {
+ "type": "int64",
+ "description": "Virtual device port number",
+ "example": 34
+ },
+ "physDeviceId": {
+ "type": "String",
+ "description": "Physical device identifier",
+ "example": "of:0000000000000003"
+ },
+ "physPortNum": {
+ "type": "int64",
+ "description": "Physical device port number",
+ "example": 2
+ }
+ }
+}
diff --git a/apps/virtual/app/src/main/resources/definitions/VirtualPorts.json b/apps/virtual/app/src/main/resources/definitions/VirtualPorts.json
new file mode 100644
index 0000000..daa5019
--- /dev/null
+++ b/apps/virtual/app/src/main/resources/definitions/VirtualPorts.json
@@ -0,0 +1,54 @@
+{
+ "type": "object",
+ "title": "VirtualPorts",
+ "required": [
+ "ports"
+ ],
+ "properties": {
+ "ports": {
+ "type": "array",
+ "xml": {
+ "name": "ports",
+ "wrapped": true
+ },
+ "items": {
+ "type": "object",
+ "title": "vport",
+ "required": [
+ "networkId",
+ "deviceId",
+ "portNum",
+ "physDeviceId",
+ "physPortNum"
+ ],
+ "properties": {
+ "networkId": {
+ "type": "int64",
+ "description": "Network identifier",
+ "example": 3
+ },
+ "deviceId": {
+ "type": "String",
+ "description": "Virtual device identifier",
+ "example": "of:0000000000000042"
+ },
+ "portNum": {
+ "type": "int64",
+ "description": "Virtual device port number",
+ "example": 34
+ },
+ "physDeviceId": {
+ "type": "String",
+ "description": "Physical device identifier",
+ "example": "of:0000000000000003"
+ },
+ "physPortNum": {
+ "type": "int64",
+ "description": "Physical device port number",
+ "example": 2
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/AbstractVirtualListenerManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/AbstractVirtualListenerManagerTest.java
new file mode 100644
index 0000000..bc0d32e
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/AbstractVirtualListenerManagerTest.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2018-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;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.event.EventListener;
+import org.onosproject.event.EventSink;
+import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.event.VirtualEvent;
+import org.onosproject.incubator.net.virtual.event.VirtualListenerRegistryManager;
+import org.onosproject.net.TenantId;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test of the virtual event dispatcher mechanism.
+ */
+public class AbstractVirtualListenerManagerTest {
+
+ private TestEventDispatcher dispatcher = new TestEventDispatcher();
+ private VirtualListenerRegistryManager listenerRegistryManager =
+ VirtualListenerRegistryManager.getInstance();
+
+ private PrickleManager prickleManager;
+ private PrickleListener prickleListener;
+
+ private GooManager gooManager;
+ private GooListener gooListener;
+
+ private BarManager barManager;
+ private BarListener barListener;
+
+ @Before
+ public void setUp() {
+ VirtualNetworkService manager = new TestVirtualNetworkManager();
+
+ dispatcher.addSink(VirtualEvent.class, listenerRegistryManager);
+
+ prickleListener = new PrickleListener();
+ prickleManager = new PrickleManager(manager, NetworkId.networkId(1));
+ prickleManager.addListener(prickleListener);
+
+ gooListener = new GooListener();
+ gooManager = new GooManager(manager, NetworkId.networkId(1));
+ gooManager.addListener(gooListener);
+
+ barListener = new BarListener();
+ barManager = new BarManager(manager, NetworkId.networkId(2));
+ barManager.addListener(barListener);
+ }
+
+ @After
+ public void tearDown() {
+ dispatcher.removeSink(VirtualEvent.class);
+
+ prickleListener.events.clear();
+ gooListener.events.clear();
+ barListener.events.clear();
+
+ prickleListener.latch = null;
+ gooListener.latch = null;
+ barListener.latch = null;
+ }
+
+ @Test
+ public void postPrickle() throws InterruptedException {
+ prickleListener.latch = new CountDownLatch(1);
+ prickleManager.post(new Prickle("prickle"));
+ prickleListener.latch.await(100, TimeUnit.MILLISECONDS);
+
+ validate(prickleListener, "prickle");
+ validate(gooListener);
+ validate(barListener);
+ }
+
+ @Test
+ public void postGoo() throws InterruptedException {
+ gooListener.latch = new CountDownLatch(1);
+ gooManager.post(new Goo("goo"));
+ gooListener.latch.await(100, TimeUnit.MILLISECONDS);
+
+ validate(prickleListener);
+ validate(gooListener, "goo");
+ validate(barListener);
+ }
+
+ @Test
+ public void postBar() throws InterruptedException {
+ barListener.latch = new CountDownLatch(1);
+ barManager.post(new Bar("bar"));
+ barListener.latch.await(100, TimeUnit.MILLISECONDS);
+
+ validate(prickleListener);
+ validate(gooListener);
+ validate(barListener, "bar");
+ }
+
+ @Test
+ public void postEventWithNoListener() throws Exception {
+ dispatcher.post(new Thing("boom"));
+
+ validate(prickleListener);
+ validate(gooListener);
+ validate(barListener);
+ }
+
+ private void validate(TestListener listener, String... strings) {
+ int i = 0;
+ assertEquals("incorrect event count", strings.length, listener.events.size());
+ for (String string : strings) {
+ Event event = (Event) listener.events.get(i++);
+ assertEquals("incorrect event", string, event.subject());
+ }
+ }
+
+ private enum Type { FOO }
+
+ private static class Thing extends AbstractEvent<Type, String> {
+ protected Thing(String subject) {
+ super(Type.FOO, subject);
+ }
+ }
+
+ private static final class Prickle extends Thing {
+ private Prickle(String subject) {
+ super(subject);
+ }
+ }
+
+ private static final class Goo extends Thing {
+ private Goo(String subject) {
+ super(subject);
+ }
+ }
+
+ private static final class Bar extends Thing {
+ private Bar(String subject) {
+ super(subject);
+ }
+ }
+
+ private class TestListener<E extends Event> implements EventListener<E> {
+ List<E> events = new ArrayList<>();
+ CountDownLatch latch;
+
+ @Override
+ public void event(E event) {
+ events.add(event);
+ latch.countDown();
+ }
+ }
+
+ private class PrickleListener extends TestListener<Prickle> {
+ }
+
+ private class GooListener extends TestListener<Goo> {
+ }
+
+ private class BarListener extends TestListener<Bar> {
+ }
+
+ private class PrickleManager extends AbstractVirtualListenerManager<Prickle, PrickleListener> {
+ public PrickleManager(VirtualNetworkService service, NetworkId networkId) {
+ super(service, networkId, Prickle.class);
+ }
+
+ @Override
+ protected void post(Prickle event) {
+ super.post(event);
+ }
+ }
+
+ private class GooManager extends AbstractVirtualListenerManager<Goo, GooListener> {
+ public GooManager(VirtualNetworkService service, NetworkId networkId) {
+ super(service, networkId, Goo.class);
+ }
+
+ @Override
+ protected void post(Goo event) {
+ super.post(event);
+ }
+ }
+
+ private class BarManager extends AbstractVirtualListenerManager<Bar, BarListener> {
+ public BarManager(VirtualNetworkService service, NetworkId networkId) {
+ super(service, networkId, Bar.class);
+ }
+
+ @Override
+ protected void post(Bar event) {
+ super.post(event);
+ }
+ }
+
+
+ private class TestEventDispatcher implements EventDeliveryService {
+ private EventSink sink;
+
+ @Override
+ public <E extends Event> void addSink(Class<E> eventClass, EventSink<E> sink) {
+ this.sink = sink;
+ }
+
+ @Override
+ public <E extends Event> void removeSink(Class<E> eventClass) {
+ this.sink = null;
+ }
+
+ @Override
+ public <E extends Event> EventSink<E> getSink(Class<E> eventClass) {
+ return null;
+ }
+
+ @Override
+ public Set<Class<? extends Event>> getSinks() {
+ return null;
+ }
+
+ @Override
+ public void setDispatchTimeLimit(long millis) {
+
+ }
+
+ @Override
+ public long getDispatchTimeLimit() {
+ return 0;
+ }
+
+ @Override
+ public void post(Event event) {
+ if (event instanceof VirtualEvent) {
+ sink.process(event);
+ }
+ }
+ }
+
+ private class TestVirtualNetworkManager extends VirtualNetworkServiceAdapter {
+ TestServiceDirectory serviceDirectory = new TestServiceDirectory();
+
+ public TestVirtualNetworkManager() {
+ serviceDirectory.add(EventDeliveryService.class, dispatcher);
+ }
+
+ @Override
+ public VirtualNetwork getVirtualNetwork(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public TenantId getTenantId(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public ServiceDirectory getServiceDirectory() {
+ return serviceDirectory;
+ }
+ }
+
+ private class TestServiceDirectory implements ServiceDirectory {
+
+ private ClassToInstanceMap<Object> services = MutableClassToInstanceMap.create();
+
+ @Override
+ public <T> T get(Class<T> serviceClass) {
+ return services.getInstance(serviceClass);
+ }
+
+ /**
+ * Adds a new service to the directory.
+ *
+ * @param serviceClass service class
+ * @param service service instance
+ * @return self
+ */
+ public TestServiceDirectory add(Class serviceClass, Object service) {
+ services.putInstance(serviceClass, service);
+ return this;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminServiceAdapter.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminServiceAdapter.java
new file mode 100644
index 0000000..4fbed2f
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminServiceAdapter.java
@@ -0,0 +1,126 @@
+/*
+ * 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;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TenantId;
+
+import java.util.Set;
+
+/**
+ * Test adapter for virtual network admin service.
+ */
+public class VirtualNetworkAdminServiceAdapter
+ extends VirtualNetworkServiceAdapter
+ implements VirtualNetworkAdminService {
+
+ @Override
+ public void registerTenantId(TenantId tenantId) {
+
+ }
+
+ @Override
+ public void unregisterTenantId(TenantId tenantId) {
+
+ }
+
+ @Override
+ public Set<TenantId> getTenantIds() {
+ return null;
+ }
+
+ @Override
+ public VirtualNetwork createVirtualNetwork(TenantId tenantId) {
+ return null;
+ }
+
+ @Override
+ public void removeVirtualNetwork(NetworkId networkId) {
+
+ }
+
+ @Override
+ public VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public void removeVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+
+ }
+
+ @Override
+ public VirtualHost createVirtualHost(NetworkId networkId, HostId hostId,
+ MacAddress mac, VlanId vlan,
+ HostLocation location, Set<IpAddress> ips) {
+ return null;
+ }
+
+ @Override
+ public void removeVirtualHost(NetworkId networkId, HostId hostId) {
+
+ }
+
+ @Override
+ public VirtualLink createVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+ return null;
+ }
+
+ @Override
+ public void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+
+ }
+
+ @Override
+ public VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+ return null;
+ }
+
+ @Override
+ public void bindVirtualPort(NetworkId networkId, DeviceId deviceId,
+ PortNumber portNumber, ConnectPoint realizedBy) {
+
+ }
+
+ @Override
+ public void updatePortState(NetworkId networkId, DeviceId deviceId, PortNumber portNumber, boolean isEnabled) {
+
+ }
+
+ @Override
+ public void removeVirtualPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+
+ }
+
+ @Override
+ public VirtualNetwork getVirtualNetwork(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public TenantId getTenantId(NetworkId networkId) {
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkServiceAdapter.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkServiceAdapter.java
new file mode 100644
index 0000000..9420d60
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/VirtualNetworkServiceAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.TenantId;
+
+import java.util.Set;
+
+/**
+ * Test adapter for virtual network service.
+ */
+public abstract class VirtualNetworkServiceAdapter implements VirtualNetworkService {
+ @Override
+ public void addListener(VirtualNetworkListener listener) {
+
+ }
+
+ @Override
+ public void removeListener(VirtualNetworkListener listener) {
+
+ }
+
+ @Override
+ public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
+ return null;
+ }
+
+ @Override
+ public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public Set<VirtualHost> getVirtualHosts(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
+ return null;
+ }
+
+ @Override
+ public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public Set<DeviceId> getPhysicalDevices(NetworkId networkId, DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public <T> T get(NetworkId networkId, Class<T> serviceClass) {
+ return null;
+ }
+
+ @Override
+ public ServiceDirectory getServiceDirectory() {
+ return null;
+ }
+
+ @Override
+ public ApplicationId getVirtualNetworkApplicationId(NetworkId networkId) {
+ return null;
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/TestCoreService.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/TestCoreService.java
new file mode 100644
index 0000000..80b74df
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/TestCoreService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.impl;
+
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Core service test class.
+ */
+class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManagerTest.java
new file mode 100644
index 0000000..63681e8
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkDeviceManagerTest.java
@@ -0,0 +1,493 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkDeviceService.
+ */
+public class VirtualNetworkDeviceManagerTest extends VirtualNetworkTestUtil {
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService;
+ private TestServiceDirectory testDirectory;
+ private TestListener testListener = new TestListener();
+ private TestEventDispatcher dispatcher = new TestEventDispatcher();
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ coreService = new VirtualNetworkDeviceManagerTest.TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, dispatcher);
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Tests the getDevices(), getAvailableDevices(), getDeviceCount(), getDevice(), and isAvailable() methods.
+ */
+ @Test
+ public void testGetDevices() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice device1 = manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice device2 = manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getDevices() method
+ Iterator<Device> it = deviceService.getDevices().iterator();
+ assertEquals("The device set size did not match.", 2, Iterators.size(it));
+
+ // test the getAvailableDevices() method
+ Iterator<Device> it2 = deviceService.getAvailableDevices().iterator();
+ assertEquals("The device set size did not match.", 2, Iterators.size(it2));
+
+ // test the getDeviceCount() method
+ assertEquals("The device set size did not match.", 2, deviceService.getDeviceCount());
+
+ // test the getDevice() method
+ assertEquals("The expect device did not match.", device1,
+ deviceService.getDevice(DID1));
+ assertNotEquals("The expect device should not have matched.", device1,
+ deviceService.getDevice(DID2));
+
+ // test the isAvailable() method
+ assertTrue("The expect device availability did not match.",
+ deviceService.isAvailable(DID1));
+ assertFalse("The expect device availability did not match.",
+ deviceService.isAvailable(DID3));
+ }
+
+ /**
+ * Tests querying for a device using a null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDeviceByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getDevice() method with null device id value.
+ deviceService.getDevice(null);
+ }
+
+ /**
+ * Tests querying for a device using a null device type.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDeviceByNullType() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getDevices() method with null type value.
+ deviceService.getDevices(null);
+ }
+
+ /**
+ * Tests the isAvailable method using a null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsAvailableByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the isAvailable() method with null device id value.
+ deviceService.isAvailable(null);
+ }
+
+ /**
+ * Tests querying for a device and available devices by device type.
+ */
+ @Test
+ public void testGetDeviceType() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getDevices(Type) method.
+ Iterator<Device> it = deviceService.getDevices(Device.Type.VIRTUAL).iterator();
+ assertEquals("The device set size did not match.", 2, Iterators.size(it));
+ Iterator<Device> it2 = deviceService.getDevices(Device.Type.SWITCH).iterator();
+ assertEquals("The device set size did not match.", 0, Iterators.size(it2));
+
+ // test the getAvailableDevices(Type) method.
+ Iterator<Device> it3 = deviceService.getAvailableDevices(Device.Type.VIRTUAL).iterator();
+ assertEquals("The device set size did not match.", 2, Iterators.size(it3));
+ }
+
+ /**
+ * Tests querying the role of a device by null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetRoleByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getRole() method using a null device identifier
+ deviceService.getRole(null);
+ }
+
+ /**
+ * Tests querying the role of a device by device identifier.
+ */
+ @Test
+ public void testGetRole() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getRole() method
+ assertEquals("The expect device role did not match.", MastershipRole.MASTER,
+ deviceService.getRole(DID1));
+ }
+
+ /**
+ * Tests querying the ports of a device by null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPortsByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getPorts() method using a null device identifier
+ deviceService.getPorts(null);
+ }
+
+ /**
+ * Tests querying the ports of a device by device identifier.
+ */
+ @Test
+ public void testGetPorts() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ ConnectPoint cp = new ConnectPoint(virtualDevice.id(), PortNumber.portNumber(1));
+
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice.id(), PortNumber.portNumber(1), cp);
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice.id(), PortNumber.portNumber(2), cp);
+
+ // test the getPorts() method
+ assertEquals("The port set size did not match.", 2,
+ deviceService.getPorts(DID1).size());
+ assertEquals("The port set size did not match.", 0,
+ deviceService.getPorts(DID2).size());
+ }
+
+ /**
+ * Tests querying the port of a device by device identifier and port number.
+ */
+ @Test
+ public void testGetPort() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ ConnectPoint cp = new ConnectPoint(virtualDevice.id(), PortNumber.portNumber(1));
+
+ VirtualPort virtualPort1 = manager.createVirtualPort(virtualNetwork.id(), virtualDevice.id(),
+ PortNumber.portNumber(1), cp);
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice.id(), PortNumber.portNumber(2), cp);
+
+ // test the getPort() method
+ assertEquals("The port did not match as expected.", virtualPort1,
+ deviceService.getPort(DID1, PortNumber.portNumber(1)));
+ assertNotEquals("The port did not match as expected.", virtualPort1,
+ deviceService.getPort(DID1, PortNumber.portNumber(3)));
+ }
+
+ /**
+ * Tests querying the port statistics of a device by null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPortsStatisticsByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getPortStatistics() method using a null device identifier
+ deviceService.getPortStatistics(null);
+ }
+
+ /**
+ * Tests querying the port statistics of a device by device identifier.
+ */
+ @Test
+ public void testGetPortStatistics() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getPortStatistics() method
+ assertEquals("The port statistics set size did not match.", 0,
+ deviceService.getPortStatistics(DID1).size());
+ }
+
+ /**
+ * Tests querying the port delta statistics of a device by null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPortsDeltaStatisticsByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getPortDeltaStatistics() method using a null device identifier
+ deviceService.getPortDeltaStatistics(null);
+ }
+
+ /**
+ * Tests querying the port delta statistics of a device by device identifier.
+ */
+ @Test
+ public void testGetPortDeltaStatistics() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // test the getPortDeltaStatistics() method
+ assertEquals("The port delta statistics set size did not match.", 0,
+ deviceService.getPortDeltaStatistics(DID1).size());
+ }
+
+ /**
+ * Tests DeviceEvents received during virtual device/port addition and removal.
+ */
+ @Test
+ public void testDeviceEventsForAddRemovalDeviceAndPorts() throws TestUtils.TestUtilsException {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ // add virtual device before virtual device manager is created
+ VirtualDevice device1 = manager.createVirtualDevice(virtualNetwork.id(), VDID1);
+ validateEvents(); // no DeviceEvent expected
+
+ testDirectory.add(EventDeliveryService.class, dispatcher);
+ DeviceService deviceService = manager.get(virtualNetwork.id(), DeviceService.class);
+
+ // virtual device manager is created; register DeviceEvent listener
+ deviceService.addListener(testListener);
+
+ // list to keep track of expected event types
+ List<DeviceEvent.Type> expectedEventTypes = new ArrayList<>();
+
+ // add virtual device
+ VirtualDevice device2 = manager.createVirtualDevice(virtualNetwork.id(), VDID2);
+ expectedEventTypes.add(DeviceEvent.Type.DEVICE_ADDED);
+
+ ConnectPoint cp = new ConnectPoint(PHYDID1, PortNumber.portNumber(1));
+
+ // add 2 virtual ports
+ manager.createVirtualPort(virtualNetwork.id(),
+ device2.id(), PortNumber.portNumber(1), cp);
+ expectedEventTypes.add(DeviceEvent.Type.PORT_ADDED);
+ manager.createVirtualPort(virtualNetwork.id(),
+ device2.id(), PortNumber.portNumber(2), cp);
+ expectedEventTypes.add(DeviceEvent.Type.PORT_ADDED);
+
+ // verify virtual ports were added
+ Set<VirtualPort> virtualPorts = manager.getVirtualPorts(virtualNetwork.id(), device2.id());
+ assertNotNull("The virtual port set should not be null", virtualPorts);
+ assertEquals("The virtual port set size did not match.", 2, virtualPorts.size());
+ virtualPorts.forEach(vp -> assertFalse("Initial virtual port state should be disabled", vp.isEnabled()));
+
+ // verify change state of virtual port (disabled -> enabled)
+ manager.updatePortState(virtualNetwork.id(), device2.id(), PortNumber.portNumber(1), true);
+ Port changedPort = deviceService.getPort(device2.id(), PortNumber.portNumber(1));
+ assertNotNull("The changed virtual port should not be null", changedPort);
+ assertEquals("Virtual port state should be enabled", true, changedPort.isEnabled());
+ expectedEventTypes.add(DeviceEvent.Type.PORT_UPDATED);
+
+ // verify change state of virtual port (disabled -> disabled)
+ manager.updatePortState(virtualNetwork.id(), device2.id(), PortNumber.portNumber(2), false);
+ changedPort = deviceService.getPort(device2.id(), PortNumber.portNumber(2));
+ assertNotNull("The changed virtual port should not be null", changedPort);
+ assertEquals("Virtual port state should be disabled", false, changedPort.isEnabled());
+ // no VIRTUAL_PORT_UPDATED event is expected - the requested state (disabled) is same as previous state.
+
+ // remove 2 virtual ports
+ for (VirtualPort virtualPort : virtualPorts) {
+ manager.removeVirtualPort(virtualNetwork.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ expectedEventTypes.add(DeviceEvent.Type.PORT_REMOVED);
+ // attempt to remove the same virtual port again - no DeviceEvent.Type.PORT_REMOVED expected.
+ manager.removeVirtualPort(virtualNetwork.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ }
+
+ // verify virtual ports were removed
+ virtualPorts = manager.getVirtualPorts(virtualNetwork.id(), device2.id());
+ assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+
+ // Add/remove one virtual port again.
+ VirtualPort virtualPort =
+ manager.createVirtualPort(virtualNetwork.id(), device2.id(),
+ PortNumber.portNumber(1), cp);
+ expectedEventTypes.add(DeviceEvent.Type.PORT_ADDED);
+
+ ConnectPoint newCp = new ConnectPoint(PHYDID3, PortNumber.portNumber(2));
+ manager.bindVirtualPort(virtualNetwork.id(), device2.id(),
+ PortNumber.portNumber(1), newCp);
+ expectedEventTypes.add(DeviceEvent.Type.PORT_UPDATED);
+
+ manager.removeVirtualPort(virtualNetwork.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ expectedEventTypes.add(DeviceEvent.Type.PORT_REMOVED);
+
+ // verify no virtual ports remain
+ virtualPorts = manager.getVirtualPorts(virtualNetwork.id(), device2.id());
+ assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+
+ // remove virtual device
+ manager.removeVirtualDevice(virtualNetwork.id(), device2.id());
+ expectedEventTypes.add(DeviceEvent.Type.DEVICE_REMOVED);
+
+ // Validate that the events were all received in the correct order.
+ validateEvents((Enum[]) expectedEventTypes.toArray(
+ new DeviceEvent.Type[expectedEventTypes.size()]));
+
+ // cleanup
+ deviceService.removeListener(testListener);
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+ }
+
+ /**
+ * Method to validate that the actual versus expected virtual network events were
+ * received correctly.
+ *
+ * @param types expected virtual network events.
+ */
+ private void validateEvents(Enum... types) {
+ TestTools.assertAfter(100, () -> {
+ int i = 0;
+ assertEquals("wrong events received", types.length, testListener.events.size());
+ for (Event event : testListener.events) {
+ assertEquals("incorrect event type", types[i], event.type());
+ i++;
+ }
+ testListener.events.clear();
+ });
+ }
+
+ /**
+ * Test listener class to receive device events.
+ */
+ private static class TestListener implements DeviceListener {
+
+ private List<DeviceEvent> events = Lists.newArrayList();
+
+ @Override
+ public void event(DeviceEvent event) {
+ events.add(event);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerTest.java
new file mode 100644
index 0000000..1def419
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.TestApplicationId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowRuleStore;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestStorageService;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Junit tests for VirtualNetworkFlowObjectiveManager.
+ */
+public class VirtualNetworkFlowObjectiveManagerTest
+ extends VirtualNetworkTestUtil {
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private ServiceDirectory testDirectory;
+ protected SimpleVirtualFlowObjectiveStore flowObjectiveStore;
+
+ private VirtualProviderManager providerRegistryService;
+ private EventDeliveryService eventDeliveryService;
+
+ private ApplicationId appId;
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ private FlowObjectiveService service1;
+ private FlowObjectiveService service2;
+
+ //FIXME: referring flowrule service, store, and provider shouldn't be here
+ private VirtualFlowRuleProvider flowRuleProvider = new TestProvider();
+ private SimpleVirtualFlowRuleStore flowRuleStore;
+ protected StorageService storageService = new TestStorageService();
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", storageService);
+ virtualNetworkManagerStore.activate();
+
+ flowObjectiveStore = new SimpleVirtualFlowObjectiveStore();
+ TestUtils.setField(flowObjectiveStore, "storageService", storageService);
+ flowObjectiveStore.activate();
+ flowRuleStore = new SimpleVirtualFlowRuleStore();
+ flowRuleStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ TestUtils.setField(manager, "coreService", coreService);
+
+ providerRegistryService = new VirtualProviderManager();
+ providerRegistryService.registerProvider(flowRuleProvider);
+
+ eventDeliveryService = new TestEventDispatcher();
+ NetTestTools.injectEventDispatcher(manager, eventDeliveryService);
+
+ appId = new TestApplicationId("FlowRuleManagerTest");
+
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(VirtualProviderRegistryService.class, providerRegistryService)
+ .add(VirtualNetworkFlowRuleStore.class, flowRuleStore)
+ .add(VirtualNetworkFlowObjectiveStore.class, flowObjectiveStore);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ vnet1 = setupVirtualNetworkTopology(manager, TID1);
+ vnet2 = setupVirtualNetworkTopology(manager, TID2);
+
+ service1 = new VirtualNetworkFlowObjectiveManager(manager, vnet1.id());
+ service2 = new VirtualNetworkFlowObjectiveManager(manager, vnet2.id());
+ }
+
+ @After
+ public void tearDownTest() {
+ manager.deactivate();
+ virtualNetworkManagerStore.deactivate();
+ }
+
+ /**
+ * Tests adding a forwarding objective.
+ */
+ @Test
+ public void forwardingObjective() {
+ TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+ TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+ ForwardingObjective forward =
+ DefaultForwardingObjective.builder()
+ .fromApp(NetTestTools.APP_ID)
+ .withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .makePermanent()
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ assertEquals("1 flowrule entry expected",
+ 1, flowRuleStore.getFlowRuleCount(vnet1.id()));
+ assertEquals("0 flowrule entry expected",
+ 0, flowRuleStore.getFlowRuleCount(vnet2.id()));
+ }
+ });
+
+ service1.forward(VDID1, forward);
+ }
+
+ /**
+ * Tests adding a next objective.
+ */
+ @Test
+ public void nextObjective() {
+ TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+ NextObjective nextObjective = DefaultNextObjective.builder()
+ .withId(service1.allocateNextId())
+ .fromApp(NetTestTools.APP_ID)
+ .addTreatment(treatment)
+ .withType(NextObjective.Type.BROADCAST)
+ .makePermanent()
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ assertEquals("1 next map entry expected",
+ 1, service1.getNextMappings().size());
+ assertEquals("0 next map entry expected",
+ 0, service2.getNextMappings().size());
+ }
+ });
+
+ service1.next(VDID1, nextObjective);
+ }
+
+ /**
+ * Tests adding a filtering objective.
+ */
+ @Test
+ public void filteringObjective() {
+ TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+ FilteringObjective filter =
+ DefaultFilteringObjective.builder()
+ .fromApp(NetTestTools.APP_ID)
+ .withMeta(treatment)
+ .makePermanent()
+ .deny()
+ .addCondition(Criteria.matchEthType(12))
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ assertEquals("1 flowrule entry expected",
+ 1,
+ flowRuleStore.getFlowRuleCount(vnet1.id()));
+ assertEquals("0 flowrule entry expected",
+ 0,
+ flowRuleStore.getFlowRuleCount(vnet2.id()));
+
+ }
+ });
+
+ service1.filter(VDID1, filter);
+ }
+
+ //TODO: More test cases for filter, forward, and next
+
+ private class TestProvider extends AbstractVirtualProvider
+ implements VirtualFlowRuleProvider {
+
+ protected TestProvider() {
+ super(new ProviderId("test", "org.onosproject.virtual.testprovider"));
+ }
+
+ @Override
+ public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
+
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerWithDistStoreTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerWithDistStoreTest.java
new file mode 100644
index 0000000..78ed454
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManagerWithDistStoreTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.onlab.junit.TestUtils;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualFlowObjectiveStore;
+
+/**
+ * Junit tests for VirtualNetworkFlowObjectiveManager using
+ * DistributedVirtualFlowObjectiveStore. This test class extends
+ * VirtualNetworkFlowObjectiveManagerTest - all the tests defined in
+ * VirtualNetworkFlowObjectiveManagerTest will run using
+ * DistributedVirtualFlowObjectiveStore.
+ */
+public class VirtualNetworkFlowObjectiveManagerWithDistStoreTest
+ extends VirtualNetworkFlowObjectiveManagerTest {
+
+ private static final String STORE_FIELDNAME_STORAGESERVICE = "storageService";
+
+ private DistributedVirtualFlowObjectiveStore distStore;
+
+ @Before
+ public void setUp() throws Exception {
+ setupDistFlowObjectiveStore();
+ super.setUp();
+ }
+
+ private void setupDistFlowObjectiveStore() throws TestUtils.TestUtilsException {
+ distStore = new DistributedVirtualFlowObjectiveStore();
+ TestUtils.setField(distStore, STORE_FIELDNAME_STORAGESERVICE, storageService);
+
+ distStore.activate();
+ flowObjectiveStore = distStore; // super.setUp() will cause Distributed store to be used.
+ }
+
+ @After
+ public void tearDown() {
+ distStore.deactivate();
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java
new file mode 100644
index 0000000..037b67f
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java
@@ -0,0 +1,533 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.TestApplicationId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowRuleStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.*;
+
+public class VirtualNetworkFlowRuleManagerTest extends VirtualNetworkTestUtil {
+ private static final int TIMEOUT = 10;
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private ServiceDirectory testDirectory;
+ private VirtualNetworkFlowRuleStore flowRuleStore;
+ private VirtualProviderManager providerRegistryService;
+
+ private EventDeliveryService eventDeliveryService;
+
+ private VirtualNetworkFlowRuleManager vnetFlowRuleService1;
+ private VirtualNetworkFlowRuleManager vnetFlowRuleService2;
+
+ private VirtualFlowRuleProvider provider = new TestProvider();
+ private VirtualFlowRuleProviderService providerService1;
+ private VirtualFlowRuleProviderService providerService2;
+
+ protected TestFlowRuleListener listener1 = new TestFlowRuleListener();
+ protected TestFlowRuleListener listener2 = new TestFlowRuleListener();
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ private ApplicationId appId;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ flowRuleStore = new SimpleVirtualFlowRuleStore();
+
+ providerRegistryService = new VirtualProviderManager();
+ providerRegistryService.registerProvider(provider);
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ TestUtils.setField(manager, "coreService", coreService);
+
+ eventDeliveryService = new TestEventDispatcher();
+ NetTestTools.injectEventDispatcher(manager, eventDeliveryService);
+
+ appId = new TestApplicationId("FlowRuleManagerTest");
+
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(VirtualProviderRegistryService.class, providerRegistryService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(VirtualNetworkFlowRuleStore.class, flowRuleStore);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ vnet1 = setupVirtualNetworkTopology(manager, TID1);
+ vnet2 = setupVirtualNetworkTopology(manager, TID2);
+
+ vnetFlowRuleService1 = new VirtualNetworkFlowRuleManager(manager, vnet1.id());
+ vnetFlowRuleService2 = new VirtualNetworkFlowRuleManager(manager, vnet2.id());
+ vnetFlowRuleService1.addListener(listener1);
+ vnetFlowRuleService2.addListener(listener2);
+
+ vnetFlowRuleService1.operationsService = MoreExecutors.newDirectExecutorService();
+ vnetFlowRuleService2.operationsService = MoreExecutors.newDirectExecutorService();
+ vnetFlowRuleService1.deviceInstallers = MoreExecutors.newDirectExecutorService();
+ vnetFlowRuleService2.deviceInstallers = MoreExecutors.newDirectExecutorService();
+
+ providerService1 = (VirtualFlowRuleProviderService)
+ providerRegistryService.getProviderService(vnet1.id(), VirtualFlowRuleProvider.class);
+ providerService2 = (VirtualFlowRuleProviderService)
+ providerRegistryService.getProviderService(vnet2.id(), VirtualFlowRuleProvider.class);
+ }
+
+ @After
+ public void tearDown() {
+ manager.deactivate();
+ virtualNetworkManagerStore.deactivate();
+ }
+
+ private FlowRule flowRule(int tsval, int trval) {
+ return flowRule(VDID1, tsval, trval);
+ }
+
+ private FlowRule flowRule(DeviceId did, int tsval, int trval) {
+ TestSelector ts = new TestSelector(tsval);
+ TestTreatment tr = new TestTreatment(trval);
+ return DefaultFlowRule.builder()
+ .forDevice(did)
+ .withSelector(ts)
+ .withTreatment(tr)
+ .withPriority(10)
+ .fromApp(appId)
+ .makeTemporary(TIMEOUT)
+ .build();
+ }
+
+ private FlowRule addFlowRule(int hval) {
+ FlowRule rule = flowRule(hval, hval);
+ vnetFlowRuleService1.applyFlowRules(rule);
+
+ assertNotNull("rule should be found", vnetFlowRuleService1.getFlowEntries(VDID1));
+ return rule;
+ }
+
+ private int flowCount(FlowRuleService service) {
+ List<FlowEntry> entries = Lists.newArrayList();
+ service.getFlowEntries(VDID1).forEach(entries::add);
+ return entries.size();
+ }
+
+ @Test
+ public void getFlowEntries() {
+ assertTrue("store should be empty",
+ Sets.newHashSet(vnetFlowRuleService1.getFlowEntries(VDID1)).isEmpty());
+ assertTrue("store should be empty",
+ Sets.newHashSet(vnetFlowRuleService2.getFlowEntries(VDID1)).isEmpty());
+
+ FlowRule f1 = addFlowRule(1);
+ FlowRule f2 = addFlowRule(2);
+
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+
+ assertEquals("2 rules should exist", 2, flowCount(vnetFlowRuleService1));
+ assertEquals("0 rules should exist", 0, flowCount(vnetFlowRuleService2));
+
+ providerService1.pushFlowMetrics(VDID1, ImmutableList.of(fe1, fe2));
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
+ RULE_ADDED, RULE_ADDED);
+
+ addFlowRule(1);
+ assertEquals("should still be 2 rules", 2, flowCount(vnetFlowRuleService1));
+ System.err.println("events :" + listener1.events);
+ assertEquals("0 rules should exist", 0, flowCount(vnetFlowRuleService2));
+
+ providerService1.pushFlowMetrics(VDID1, ImmutableList.of(fe1));
+ validateEvents(listener1, RULE_UPDATED, RULE_UPDATED);
+ }
+
+ @Test
+ public void applyFlowRules() {
+ FlowRule r1 = flowRule(1, 1);
+ FlowRule r2 = flowRule(2, 2);
+ FlowRule r3 = flowRule(3, 3);
+
+ assertTrue("store should be empty",
+ Sets.newHashSet(vnetFlowRuleService1.getFlowEntries(DID1)).isEmpty());
+ vnetFlowRuleService1.applyFlowRules(r1, r2, r3);
+ assertEquals("3 rules should exist", 3, flowCount(vnetFlowRuleService1));
+ assertTrue("Entries should be pending add.",
+ validateState(ImmutableMap.of(
+ r1, FlowEntry.FlowEntryState.PENDING_ADD,
+ r2, FlowEntry.FlowEntryState.PENDING_ADD,
+ r3, FlowEntry.FlowEntryState.PENDING_ADD)));
+ }
+
+ @Test
+ public void purgeFlowRules() {
+ FlowRule f1 = addFlowRule(1);
+ FlowRule f2 = addFlowRule(2);
+ FlowRule f3 = addFlowRule(3);
+ assertEquals("3 rules should exist", 3, flowCount(vnetFlowRuleService1));
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
+ providerService1.pushFlowMetrics(VDID1, ImmutableList.of(fe1, fe2, fe3));
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
+ RULE_ADDED, RULE_ADDED, RULE_ADDED);
+ vnetFlowRuleService1.purgeFlowRules(VDID1);
+ assertEquals("0 rule should exist", 0, flowCount(vnetFlowRuleService1));
+ }
+
+ @Test
+ public void removeFlowRules() {
+ FlowRule f1 = addFlowRule(1);
+ FlowRule f2 = addFlowRule(2);
+ FlowRule f3 = addFlowRule(3);
+ assertEquals("3 rules should exist", 3, flowCount(vnetFlowRuleService1));
+
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
+ providerService1.pushFlowMetrics(VDID1, ImmutableList.of(fe1, fe2, fe3));
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
+ RULE_ADDED, RULE_ADDED, RULE_ADDED);
+
+ vnetFlowRuleService1.removeFlowRules(f1, f2);
+ //removing from north, so no events generated
+ validateEvents(listener1, RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED);
+ assertEquals("3 rule should exist", 3, flowCount(vnetFlowRuleService1));
+ assertTrue("Entries should be pending remove.",
+ validateState(ImmutableMap.of(
+ f1, FlowEntry.FlowEntryState.PENDING_REMOVE,
+ f2, FlowEntry.FlowEntryState.PENDING_REMOVE,
+ f3, FlowEntry.FlowEntryState.ADDED)));
+
+ vnetFlowRuleService1.removeFlowRules(f1);
+ assertEquals("3 rule should still exist", 3, flowCount(vnetFlowRuleService1));
+ }
+
+ @Test
+ public void flowRemoved() {
+ FlowRule f1 = addFlowRule(1);
+ FlowRule f2 = addFlowRule(2);
+ StoredFlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+
+ providerService1.pushFlowMetrics(VDID1, ImmutableList.of(fe1, fe2));
+ vnetFlowRuleService1.removeFlowRules(f1);
+
+ //FIXME modification of "stored" flow entry outside of store
+ fe1.setState(FlowEntry.FlowEntryState.REMOVED);
+
+ providerService1.flowRemoved(fe1);
+
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED,
+ RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED);
+
+ providerService1.flowRemoved(fe1);
+ validateEvents(listener1);
+
+ FlowRule f3 = flowRule(3, 3);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
+ vnetFlowRuleService1.applyFlowRules(f3);
+
+ providerService1.pushFlowMetrics(VDID1, Collections.singletonList(fe3));
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADDED, RULE_UPDATED);
+
+ providerService1.flowRemoved(fe3);
+ validateEvents(listener1);
+ }
+
+ @Test
+ public void extraneousFlow() {
+ FlowRule f1 = flowRule(1, 1);
+ FlowRule f2 = flowRule(2, 2);
+ FlowRule f3 = flowRule(3, 3);
+ vnetFlowRuleService1.applyFlowRules(f1, f2);
+
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ FlowEntry fe3 = new DefaultFlowEntry(f3);
+
+
+ providerService1.pushFlowMetrics(VDID1, Lists.newArrayList(fe1, fe2, fe3));
+
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
+ RULE_ADDED, RULE_ADDED);
+ }
+
+ /*
+ * Tests whether a rule that was marked for removal but no flowRemoved was received
+ * is indeed removed at the next stats update.
+ */
+ @Test
+ public void flowMissingRemove() {
+ FlowRule f1 = flowRule(1, 1);
+ FlowRule f2 = flowRule(2, 2);
+ FlowRule f3 = flowRule(3, 3);
+
+ FlowEntry fe1 = new DefaultFlowEntry(f1);
+ FlowEntry fe2 = new DefaultFlowEntry(f2);
+ vnetFlowRuleService1.applyFlowRules(f1, f2, f3);
+
+ vnetFlowRuleService1.removeFlowRules(f3);
+
+ providerService1.pushFlowMetrics(VDID1, Lists.newArrayList(fe1, fe2));
+
+ validateEvents(listener1, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
+ RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED);
+ }
+
+ @Test
+ public void removeByAppId() {
+ FlowRule f1 = flowRule(1, 1);
+ FlowRule f2 = flowRule(2, 2);
+ vnetFlowRuleService1.applyFlowRules(f1, f2);
+
+ vnetFlowRuleService1.removeFlowRulesById(appId);
+
+ //only check that we are in pending remove. Events and actual remove state will
+ // be set by flowRemoved call.
+ validateState(ImmutableMap.of(
+ f1, FlowEntry.FlowEntryState.PENDING_REMOVE,
+ f2, FlowEntry.FlowEntryState.PENDING_REMOVE));
+ }
+
+ //TODO:Tests for fallback
+
+ private boolean validateState(Map<FlowRule, FlowEntry.FlowEntryState> expected) {
+ Map<FlowRule, FlowEntry.FlowEntryState> expectedToCheck = new HashMap<>(expected);
+ Iterable<FlowEntry> rules = vnetFlowRuleService1.getFlowEntries(VDID1);
+ for (FlowEntry f : rules) {
+ assertTrue("Unexpected FlowRule " + f, expectedToCheck.containsKey(f));
+ assertEquals("FlowEntry" + f, expectedToCheck.get(f), f.state());
+ expectedToCheck.remove(f);
+ }
+ assertEquals(Collections.emptySet(), expectedToCheck.entrySet());
+ return true;
+ }
+
+ private class TestSelector implements TrafficSelector {
+
+ //for controlling hashcode uniqueness;
+ private final int testval;
+
+ public TestSelector(int val) {
+ testval = val;
+ }
+
+ @Override
+ public Set<Criterion> criteria() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Criterion getCriterion(
+ org.onosproject.net.flow.criteria.Criterion.Type type) {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return testval;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof TestSelector) {
+ return this.testval == ((TestSelector) o).testval;
+ }
+ return false;
+ }
+ }
+
+ private class TestTreatment implements TrafficTreatment {
+
+ //for controlling hashcode uniqueness;
+ private final int testval;
+
+ public TestTreatment(int val) {
+ testval = val;
+ }
+
+ @Override
+ public List<Instruction> deferred() {
+ return null;
+ }
+
+ @Override
+ public List<Instruction> immediate() {
+ return null;
+ }
+
+ @Override
+ public List<Instruction> allInstructions() {
+ return null;
+ }
+
+ @Override
+ public Instructions.TableTypeTransition tableTransition() {
+ return null;
+ }
+
+ @Override
+ public boolean clearedDeferred() {
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return testval;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof TestTreatment) {
+ return this.testval == ((TestTreatment) o).testval;
+ }
+ return false;
+ }
+
+ @Override
+ public Instructions.MetadataInstruction writeMetadata() {
+ return null;
+ }
+
+ @Override
+ public Instructions.StatTriggerInstruction statTrigger() {
+ return null;
+ }
+
+ @Override
+ public Instructions.MeterInstruction metered() {
+ return null;
+ }
+
+ @Override
+ public Set<Instructions.MeterInstruction> meters() {
+ return Sets.newHashSet();
+ }
+ }
+
+ private void validateEvents(TestFlowRuleListener listener, FlowRuleEvent.Type... events) {
+ if (events == null) {
+ assertTrue("events generated", listener.events.isEmpty());
+ }
+
+ int i = 0;
+ System.err.println("events :" + listener.events);
+ for (FlowRuleEvent e : listener.events) {
+ assertEquals("unexpected event", events[i], e.type());
+ i++;
+ }
+
+ assertEquals("mispredicted number of events",
+ events.length, listener.events.size());
+
+ listener.events.clear();
+ }
+
+ private class TestFlowRuleListener implements FlowRuleListener {
+
+ public final List<FlowRuleEvent> events = new ArrayList<>();
+
+ @Override
+ public void event(FlowRuleEvent event) {
+ events.add(event);
+ }
+ }
+
+ private class TestProvider extends AbstractVirtualProvider
+ implements VirtualFlowRuleProvider {
+
+ protected TestProvider() {
+ super(new ProviderId("test", "org.onosproject.virtual.testprovider"));
+ }
+
+ @Override
+ public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
+
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManagerTest.java
new file mode 100644
index 0000000..7aa0d9c
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkGroupManagerTest.java
@@ -0,0 +1,702 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Iterables;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onosproject.TestApplicationId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.GroupId;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualGroupProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualGroupProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualGroupStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.StoredGroupEntry;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.onosproject.incubator.net.virtual.impl.VirtualNetworkTestUtil.*;
+import static org.onosproject.net.NetTestTools.injectEventDispatcher;
+
+/**
+ * Test codifying the virtual group service & group provider service contracts.
+ */
+public class VirtualNetworkGroupManagerTest {
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private ServiceDirectory testDirectory;
+ private VirtualProviderManager providerRegistryService;
+
+ private EventDeliveryService eventDeliveryService;
+
+ private VirtualNetworkGroupManager groupManager1;
+ private VirtualNetworkGroupManager groupManager2;
+
+ private VirtualNetworkGroupStore groupStore;
+
+ private TestGroupProvider provider = new TestGroupProvider();
+ private VirtualGroupProviderService providerService1;
+ private VirtualGroupProviderService providerService2;
+
+ protected TestGroupListener listener1 = new TestGroupListener();
+ protected TestGroupListener listener2 = new TestGroupListener();
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ private ApplicationId appId;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ groupStore = new SimpleVirtualGroupStore();
+
+ providerRegistryService = new VirtualProviderManager();
+ providerRegistryService.registerProvider(provider);
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ TestUtils.setField(manager, "coreService", coreService);
+
+ eventDeliveryService = new TestEventDispatcher();
+ injectEventDispatcher(manager, eventDeliveryService);
+
+ appId = new TestApplicationId("VirtualGroupManagerTest");
+
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(VirtualProviderRegistryService.class, providerRegistryService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(VirtualNetworkGroupStore.class, groupStore);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ vnet1 = setupVirtualNetworkTopology(manager, TID1);
+ vnet2 = setupVirtualNetworkTopology(manager, TID2);
+
+ groupManager1 = new VirtualNetworkGroupManager(manager, vnet1.id());
+ groupManager2 = new VirtualNetworkGroupManager(manager, vnet2.id());
+ groupManager1.addListener(listener1);
+ groupManager2.addListener(listener2);
+
+ providerService1 = (VirtualGroupProviderService)
+ providerRegistryService.getProviderService(vnet1.id(),
+ VirtualGroupProvider.class);
+ providerService2 = (VirtualGroupProviderService)
+ providerRegistryService.getProviderService(vnet2.id(),
+ VirtualGroupProvider.class);
+ }
+
+ @After
+ public void tearDown() {
+ providerRegistryService.unregisterProvider(provider);
+ assertFalse("provider should not be registered",
+ providerRegistryService.getProviders().contains(provider.id()));
+ groupManager1.removeListener(listener1);
+ groupManager2.removeListener(listener2);
+
+ manager.deactivate();
+ virtualNetworkManagerStore.deactivate();
+ }
+
+ /**
+ * Tests group creation before the device group AUDIT completes.
+ */
+ @Test
+ public void testGroupServiceBasics() {
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests initial device group AUDIT process.
+ */
+ @Test
+ public void testGroupServiceInitialAudit() {
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+ // Test initial group audit process
+ testInitialAuditWithPendingGroupRequests(vnet1.id(), VDID1);
+ testInitialAuditWithPendingGroupRequests(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests deletion process of any extraneous groups.
+ */
+ @Test
+ public void testGroupServiceAuditExtraneous() {
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+
+ // Test audit with extraneous and missing groups
+ testAuditWithExtraneousMissingGroups(vnet1.id(), VDID1);
+ testAuditWithExtraneousMissingGroups(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests re-apply process of any missing groups tests execution of
+ * any pending group creation request after the device group AUDIT completes
+ * and tests event notifications after receiving confirmation for any
+ * operations from data plane.
+ */
+ @Test
+ public void testGroupServiceAuditConfirmed() {
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+
+ // Test audit with extraneous and missing groups
+ testAuditWithExtraneousMissingGroups(vnet1.id(), VDID1);
+ testAuditWithExtraneousMissingGroups(vnet2.id(), VDID1);
+
+ // Test audit with confirmed groups
+ testAuditWithConfirmedGroups(vnet1.id(), VDID1);
+ testAuditWithConfirmedGroups(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests group Purge Operation.
+ */
+ @Test
+ public void testPurgeGroups() {
+ // Tests for virtual network 1
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+ testAuditWithExtraneousMissingGroups(vnet1.id(), VDID1);
+ // Test group add bucket operations
+ testAddBuckets(vnet1.id(), VDID1);
+ // Test group Purge operations
+ testPurgeGroupEntry(vnet1.id(), VDID1);
+
+ // Tests for virtual network 2
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+ testAuditWithExtraneousMissingGroups(vnet2.id(), VDID1);
+ // Test group add bucket operations
+ testAddBuckets(vnet2.id(), VDID1);
+ // Test group Purge operations
+ testPurgeGroupEntry(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests group bucket modifications (additions and deletions) and
+ * Tests group deletion.
+ */
+ @Test
+ public void testGroupServiceBuckets() {
+ // Tests for virtual network 1
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID1);
+
+ testAuditWithExtraneousMissingGroups(vnet1.id(), VDID1);
+ // Test group add bucket operations
+ testAddBuckets(vnet1.id(), VDID1);
+
+ // Test group remove bucket operations
+ testRemoveBuckets(vnet1.id(), VDID1);
+
+ // Test group remove operations
+ testRemoveGroup(vnet1.id(), VDID1);
+
+ // Tests for virtual network 2
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet2.id(), VDID1);
+
+ testAuditWithExtraneousMissingGroups(vnet2.id(), VDID1);
+ // Test group add bucket operations
+ testAddBuckets(vnet2.id(), VDID1);
+
+ // Test group remove bucket operations
+ testRemoveBuckets(vnet2.id(), VDID1);
+
+ // Test group remove operations
+ testRemoveGroup(vnet2.id(), VDID1);
+ }
+
+ /**
+ * Tests group creation before the device group AUDIT completes with fallback
+ * provider.
+ */
+ @Test
+ public void testGroupServiceFallbackBasics() {
+ // Test Group creation before AUDIT process
+ testGroupCreationBeforeAudit(vnet1.id(), VDID2);
+ testGroupCreationBeforeAudit(vnet2.id(), VDID2);
+ }
+
+ // Test Group creation before AUDIT process
+ private void testGroupCreationBeforeAudit(NetworkId networkId, DeviceId deviceId) {
+ PortNumber[] ports1 = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ PortNumber[] ports2 = {PortNumber.portNumber(41),
+ PortNumber.portNumber(42)};
+ GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
+ List<GroupBucket> buckets = new ArrayList<>();
+ List<PortNumber> outPorts = new ArrayList<>();
+ outPorts.addAll(Arrays.asList(ports1));
+ outPorts.addAll(Arrays.asList(ports2));
+ for (PortNumber portNumber : outPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(106));
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets groupBuckets = new GroupBuckets(buckets);
+ GroupDescription newGroupDesc = new DefaultGroupDescription(deviceId,
+ Group.Type.SELECT,
+ groupBuckets,
+ key,
+ null,
+ appId);
+ VirtualNetworkGroupManager groupManager;
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ } else {
+ groupManager = groupManager2;
+ }
+
+ groupManager.addGroup(newGroupDesc);
+ assertEquals(null, groupManager.getGroup(deviceId, key));
+ assertEquals(0, Iterables.size(groupManager.getGroups(deviceId, appId)));
+ }
+
+
+ // Test initial AUDIT process with pending group requests
+ private void testInitialAuditWithPendingGroupRequests(NetworkId networkId,
+ DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ }
+
+ PortNumber[] ports1 = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ PortNumber[] ports2 = {PortNumber.portNumber(41),
+ PortNumber.portNumber(42)};
+ GroupId gId1 = new GroupId(1);
+ Group group1 = createSouthboundGroupEntry(gId1,
+ Arrays.asList(ports1),
+ 0, deviceId);
+ GroupId gId2 = new GroupId(2);
+ // Non zero reference count will make the group manager to queue
+ // the extraneous groups until reference count is zero.
+ Group group2 = createSouthboundGroupEntry(gId2,
+ Arrays.asList(ports2),
+ 2, deviceId);
+ List<Group> groupEntries = Arrays.asList(group1, group2);
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ // First group metrics would trigger the device audit completion
+ // post which all pending group requests are also executed.
+ GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
+ Group createdGroup = groupManager.getGroup(deviceId, key);
+ int createdGroupId = createdGroup.id().id();
+ assertNotEquals(gId1.id().intValue(), createdGroupId);
+ assertNotEquals(gId2.id().intValue(), createdGroupId);
+
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
+ GroupOperation.createDeleteGroupOperation(gId1,
+ Group.Type.SELECT),
+ GroupOperation.createAddGroupOperation(
+ createdGroup.id(),
+ Group.Type.SELECT,
+ createdGroup.buckets()));
+ if (deviceId.equals(VDID1)) {
+ provider.validate(networkId, deviceId, expectedGroupOps);
+ }
+ }
+
+ // Test AUDIT process with extraneous groups and missing groups
+ private void testAuditWithExtraneousMissingGroups(NetworkId networkId,
+ DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ }
+
+ PortNumber[] ports1 = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ PortNumber[] ports2 = {PortNumber.portNumber(41),
+ PortNumber.portNumber(42)};
+ GroupId gId1 = new GroupId(1);
+ Group group1 = createSouthboundGroupEntry(gId1,
+ Arrays.asList(ports1),
+ 0, deviceId);
+ GroupId gId2 = new GroupId(2);
+ Group group2 = createSouthboundGroupEntry(gId2,
+ Arrays.asList(ports2),
+ 0, deviceId);
+ List<Group> groupEntries = Arrays.asList(group1, group2);
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
+ Group createdGroup = groupManager.getGroup(deviceId, key);
+ List<GroupOperation> expectedGroupOps = Arrays.asList(
+ GroupOperation.createDeleteGroupOperation(gId1,
+ Group.Type.SELECT),
+ GroupOperation.createDeleteGroupOperation(gId2,
+ Group.Type.SELECT),
+ GroupOperation.createAddGroupOperation(createdGroup.id(),
+ Group.Type.SELECT,
+ createdGroup.buckets()));
+ if (deviceId.equals(VDID1)) {
+ provider.validate(networkId, deviceId, expectedGroupOps);
+ }
+ }
+
+ // Test AUDIT with confirmed groups
+ private void testAuditWithConfirmedGroups(NetworkId networkId,
+ DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ TestGroupListener listener;
+
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ listener = listener1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ listener = listener2;
+ }
+
+ GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
+ Group createdGroup = groupManager.getGroup(deviceId, key);
+ createdGroup = new DefaultGroup(createdGroup.id(),
+ deviceId,
+ Group.Type.SELECT,
+ createdGroup.buckets());
+ List<Group> groupEntries = Collections.singletonList(createdGroup);
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ listener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADDED));
+ }
+
+ private Group createSouthboundGroupEntry(GroupId gId,
+ List<PortNumber> ports,
+ long referenceCount, DeviceId deviceId) {
+ List<PortNumber> outPorts = new ArrayList<>();
+ outPorts.addAll(ports);
+
+ List<GroupBucket> buckets = new ArrayList<>();
+ for (PortNumber portNumber : outPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(106));
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets groupBuckets = new GroupBuckets(buckets);
+ StoredGroupEntry group = new DefaultGroup(
+ gId, deviceId, Group.Type.SELECT, groupBuckets);
+ group.setReferenceCount(referenceCount);
+ return group;
+ }
+
+ // Test group add bucket operations
+ private void testAddBuckets(NetworkId networkId, DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ TestGroupListener listener;
+
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ listener = listener1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ listener = listener2;
+ }
+
+ GroupKey addKey = new DefaultGroupKey("group1AddBuckets".getBytes());
+
+ GroupKey prevKey = new DefaultGroupKey("group1BeforeAudit".getBytes());
+ Group createdGroup = groupManager.getGroup(deviceId, prevKey);
+ List<GroupBucket> buckets = new ArrayList<>();
+ buckets.addAll(createdGroup.buckets().buckets());
+
+ PortNumber[] addPorts = {PortNumber.portNumber(51),
+ PortNumber.portNumber(52)};
+ List<PortNumber> outPorts;
+ outPorts = new ArrayList<>();
+ outPorts.addAll(Arrays.asList(addPorts));
+ List<GroupBucket> addBuckets;
+ addBuckets = new ArrayList<>();
+ for (PortNumber portNumber : outPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(106));
+ addBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
+ groupManager.addBucketsToGroup(deviceId,
+ prevKey,
+ groupAddBuckets,
+ addKey,
+ appId);
+ GroupBuckets updatedBuckets = new GroupBuckets(buckets);
+ List<GroupOperation> expectedGroupOps = Collections.singletonList(
+ GroupOperation.createModifyGroupOperation(createdGroup.id(),
+ Group.Type.SELECT,
+ updatedBuckets));
+ if (deviceId.equals(VDID1)) {
+ provider.validate(networkId, deviceId, expectedGroupOps);
+ }
+
+ Group existingGroup = groupManager.getGroup(deviceId, addKey);
+ List<Group> groupEntries = Collections.singletonList(existingGroup);
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ listener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED));
+ }
+
+ // Test purge group entry operations
+ private void testPurgeGroupEntry(NetworkId networkId, DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ } else {
+ groupManager = groupManager2;
+ }
+
+ assertEquals(1, Iterables.size(groupManager.getGroups(deviceId, appId)));
+ groupManager.purgeGroupEntries(deviceId);
+ assertEquals(0, Iterables.size(groupManager.getGroups(deviceId, appId)));
+ }
+
+ // Test group remove bucket operations
+ private void testRemoveBuckets(NetworkId networkId, DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ TestGroupListener listener;
+
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ listener = listener1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ listener = listener2;
+ }
+
+ GroupKey removeKey = new DefaultGroupKey("group1RemoveBuckets".getBytes());
+
+ GroupKey prevKey = new DefaultGroupKey("group1AddBuckets".getBytes());
+ Group createdGroup = groupManager.getGroup(deviceId, prevKey);
+ List<GroupBucket> buckets = new ArrayList<>();
+ buckets.addAll(createdGroup.buckets().buckets());
+
+ PortNumber[] removePorts = {PortNumber.portNumber(31),
+ PortNumber.portNumber(32)};
+ List<PortNumber> outPorts = new ArrayList<>();
+ outPorts.addAll(Arrays.asList(removePorts));
+ List<GroupBucket> removeBuckets = new ArrayList<>();
+ for (PortNumber portNumber : outPorts) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(portNumber)
+ .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
+ .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
+ .pushMpls()
+ .setMpls(MplsLabel.mplsLabel(106));
+ removeBuckets.add(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ buckets.remove(DefaultGroupBucket.createSelectGroupBucket(
+ tBuilder.build()));
+ }
+ GroupBuckets groupRemoveBuckets = new GroupBuckets(removeBuckets);
+ groupManager.removeBucketsFromGroup(deviceId,
+ prevKey,
+ groupRemoveBuckets,
+ removeKey,
+ appId);
+ GroupBuckets updatedBuckets = new GroupBuckets(buckets);
+ List<GroupOperation> expectedGroupOps = Collections.singletonList(
+ GroupOperation.createModifyGroupOperation(createdGroup.id(),
+ Group.Type.SELECT,
+ updatedBuckets));
+ if (deviceId.equals(VDID1)) {
+ provider.validate(networkId, deviceId, expectedGroupOps);
+ }
+
+ Group existingGroup = groupManager.getGroup(deviceId, removeKey);
+ List<Group> groupEntries = Collections.singletonList(existingGroup);
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ listener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED));
+ }
+
+ // Test group remove operations
+ private void testRemoveGroup(NetworkId networkId, DeviceId deviceId) {
+ VirtualNetworkGroupManager groupManager;
+ VirtualGroupProviderService providerService;
+ TestGroupListener listener;
+
+ if (networkId.id() == 1) {
+ groupManager = groupManager1;
+ providerService = providerService1;
+ listener = listener1;
+ } else {
+ groupManager = groupManager2;
+ providerService = providerService2;
+ listener = listener2;
+ }
+
+ GroupKey currKey = new DefaultGroupKey("group1RemoveBuckets".getBytes());
+ Group existingGroup = groupManager.getGroup(deviceId, currKey);
+ groupManager.removeGroup(deviceId, currKey, appId);
+ List<GroupOperation> expectedGroupOps = Collections.singletonList(
+ GroupOperation.createDeleteGroupOperation(existingGroup.id(),
+ Group.Type.SELECT));
+ if (deviceId.equals(VDID1)) {
+ provider.validate(networkId, deviceId, expectedGroupOps);
+ }
+
+ List<Group> groupEntries = Collections.emptyList();
+ providerService.pushGroupMetrics(deviceId, groupEntries);
+ listener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVED));
+ }
+
+ private class TestGroupProvider extends AbstractVirtualProvider
+ implements VirtualGroupProvider {
+ NetworkId lastNetworkId;
+ DeviceId lastDeviceId;
+ List<GroupOperation> groupOperations = new ArrayList<>();
+
+ protected TestGroupProvider() {
+ super(new ProviderId("test", "org.onosproject.virtual.testprovider"));
+ }
+
+ @Override
+ public void performGroupOperation(NetworkId networkId, DeviceId deviceId,
+ GroupOperations groupOps) {
+ lastNetworkId = networkId;
+ lastDeviceId = deviceId;
+ groupOperations.addAll(groupOps.operations());
+ }
+
+ public void validate(NetworkId expectedNetworkId, DeviceId expectedDeviceId,
+ List<GroupOperation> expectedGroupOps) {
+ if (expectedGroupOps == null) {
+ assertTrue("events generated", groupOperations.isEmpty());
+ return;
+ }
+
+ assertEquals(lastNetworkId, expectedNetworkId);
+ assertEquals(lastDeviceId, expectedDeviceId);
+ assertTrue((this.groupOperations.containsAll(expectedGroupOps) &&
+ expectedGroupOps.containsAll(groupOperations)));
+
+ groupOperations.clear();
+ lastDeviceId = null;
+ lastNetworkId = null;
+ }
+ }
+
+ private static class TestGroupListener implements GroupListener {
+ final List<GroupEvent> events = new ArrayList<>();
+
+ @Override
+ public void event(GroupEvent event) {
+ events.add(event);
+ }
+
+ public void validateEvent(List<GroupEvent.Type> expectedEvents) {
+ int i = 0;
+ System.err.println("events :" + events);
+ for (GroupEvent e : events) {
+ assertEquals("unexpected event", expectedEvents.get(i), e.type());
+ i++;
+ }
+ assertEquals("mispredicted number of events",
+ expectedEvents.size(), events.size());
+ events.clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManagerTest.java
new file mode 100644
index 0000000..e60d343
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkHostManagerTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Iterators;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkHostService.
+ */
+public class VirtualNetworkHostManagerTest extends TestDeviceParams {
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private TestServiceDirectory testDirectory;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Sets up a virtual network with hosts.
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupVnet() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ ConnectPoint hostCp1 = new ConnectPoint(DID1, P1);
+ ConnectPoint hostCp2 = new ConnectPoint(DID2, P2);
+ manager.createVirtualPort(virtualNetwork.id(), hostCp1.deviceId(), hostCp1.port(),
+ new ConnectPoint(virtualDevice1.id(), hostCp1.port()));
+ manager.createVirtualPort(virtualNetwork.id(), hostCp2.deviceId(), hostCp2.port(),
+ new ConnectPoint(virtualDevice2.id(), hostCp2.port()));
+
+ manager.createVirtualHost(virtualNetwork.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ manager.createVirtualHost(virtualNetwork.id(), HID2, MAC2, VLAN2, LOC2, IPSET2);
+ return virtualNetwork;
+ }
+
+ /**
+ * Sets up a virtual network with no hosts.
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupEmptyVnet() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ ConnectPoint hostCp1 = new ConnectPoint(DID1, P1);
+ ConnectPoint hostCp2 = new ConnectPoint(DID2, P2);
+ manager.createVirtualPort(virtualNetwork.id(), hostCp1.deviceId(), hostCp1.port(),
+ new ConnectPoint(virtualDevice1.id(), hostCp1.port()));
+ manager.createVirtualPort(virtualNetwork.id(), hostCp2.deviceId(), hostCp2.port(),
+ new ConnectPoint(virtualDevice2.id(), hostCp2.port()));
+
+ return virtualNetwork;
+ }
+
+ /**
+ * Tests the getHosts(), getHost(), getHostsByXX(), getConnectedHosts() methods
+ * on a non-empty virtual network.
+ */
+ @Test
+ public void testGetHostsOnNonEmptyVnet() {
+ VirtualNetwork virtualNetwork = setupEmptyVnet();
+ VirtualHost vhost1 = manager.createVirtualHost(virtualNetwork.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ VirtualHost vhost2 = manager.createVirtualHost(virtualNetwork.id(), HID2, MAC2, VLAN2, LOC2, IPSET2);
+ HostService hostService = manager.get(virtualNetwork.id(), HostService.class);
+
+ // test the getHosts() and getHostCount() methods
+ Iterator<Host> itHosts = hostService.getHosts().iterator();
+ assertEquals("The host set size did not match.", 2, Iterators.size(itHosts));
+ assertEquals("The host count did not match.", 2, hostService.getHostCount());
+
+ // test the getHost() method
+ Host testHost = hostService.getHost(HID2);
+ assertEquals("The expected host did not match.", vhost2, testHost);
+
+ // test the getHostsByVlan(...) method
+ Collection<Host> collHost = hostService.getHostsByVlan(VLAN1);
+ assertEquals("The host set size did not match.", 1, collHost.size());
+ assertTrue("The host did not match.", collHost.contains(vhost1));
+
+ // test the getHostsByMac(...) method
+ collHost = hostService.getHostsByMac(MAC2);
+ assertEquals("The host set size did not match.", 1, collHost.size());
+ assertTrue("The host did not match.", collHost.contains(vhost2));
+
+ // test the getHostsByIp(...) method
+ collHost = hostService.getHostsByIp(IP1);
+ assertEquals("The host set size did not match.", 2, collHost.size());
+ collHost = hostService.getHostsByIp(IP2);
+ assertEquals("The host set size did not match.", 1, collHost.size());
+ assertTrue("The host did not match.", collHost.contains(vhost1));
+
+ // test the getConnectedHosts(ConnectPoint) method
+ collHost = hostService.getConnectedHosts(LOC1);
+ assertEquals("The host set size did not match.", 1, collHost.size());
+ assertTrue("The host did not match.", collHost.contains(vhost1));
+
+ // test the getConnectedHosts(DeviceId) method
+ collHost = hostService.getConnectedHosts(DID2);
+ assertEquals("The host set size did not match.", 1, collHost.size());
+ assertTrue("The host did not match.", collHost.contains(vhost2));
+ }
+
+ /**
+ * Tests the getHosts(), getHost(), getHostsByXX(), getConnectedHosts() methods
+ * on an empty virtual network.
+ */
+ @Test
+ public void testGetHostsOnEmptyVnet() {
+ VirtualNetwork virtualNetwork = setupEmptyVnet();
+ HostService hostService = manager.get(virtualNetwork.id(), HostService.class);
+
+ // test the getHosts() and getHostCount() methods
+ Iterator<Host> itHosts = hostService.getHosts().iterator();
+ assertEquals("The host set size did not match.", 0, Iterators.size(itHosts));
+ assertEquals("The host count did not match.", 0, hostService.getHostCount());
+
+ // test the getHost() method
+ Host testHost = hostService.getHost(HID2);
+ assertNull("The host should be null.", testHost);
+
+ // test the getHostsByVlan(...) method
+ Collection<Host> collHost = hostService.getHostsByVlan(VLAN1);
+ assertEquals("The host set size did not match.", 0, collHost.size());
+
+ // test the getHostsByMac(...) method
+ collHost = hostService.getHostsByMac(MAC2);
+ assertEquals("The host set size did not match.", 0, collHost.size());
+
+ // test the getHostsByIp(...) method
+ collHost = hostService.getHostsByIp(IP1);
+ assertEquals("The host set size did not match.", 0, collHost.size());
+
+ // test the getConnectedHosts(ConnectPoint) method
+ collHost = hostService.getConnectedHosts(LOC1);
+ assertEquals("The host set size did not match.", 0, collHost.size());
+
+ // test the getConnectedHosts(DeviceId) method
+ collHost = hostService.getConnectedHosts(DID2);
+ assertEquals("The host set size did not match.", 0, collHost.size());
+ }
+
+ /**
+ * Tests querying for a host using a null host identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetHostByNullId() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getHost(null);
+ }
+
+ /**
+ * Tests querying for hosts with null mac.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetHostsByNullMac() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getHostsByMac(null);
+ }
+
+ /**
+ * Tests querying for hosts with null vlan.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetHostsByNullVlan() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getHostsByVlan(null);
+ }
+
+ /**
+ * Tests querying for hosts with null ip.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetHostsByNullIp() {
+ VirtualNetwork vnet = setupVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getHostsByIp(null);
+ }
+
+ /**
+ * Tests querying for connected hosts with null host location (connect point).
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetConnectedHostsByNullLoc() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getConnectedHosts((ConnectPoint) null);
+ }
+
+ /**
+ * Tests querying for connected hosts with null device id.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetConnectedHostsByNullDeviceId() {
+ VirtualNetwork vnet = setupVnet();
+ HostService hostService = manager.get(vnet.id(), HostService.class);
+
+ hostService.getConnectedHosts((DeviceId) null);
+ }
+
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
new file mode 100644
index 0000000..ba0adda
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
@@ -0,0 +1,414 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.TestApplicationId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualIntentStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentTestsMocks;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.intent.WorkPartitionService;
+import org.onosproject.net.intent.WorkPartitionServiceAdapter;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Junit tests for VirtualNetworkIntentService.
+ */
+@Ignore("deprecated prototype implementation")
+public class VirtualNetworkIntentManagerTest extends TestDeviceParams {
+
+ private final String tenantIdValue1 = "TENANT_ID1";
+ private static final ApplicationId APP_ID =
+ new TestApplicationId("MyAppId");
+
+ private ConnectPoint cp1;
+ private ConnectPoint cp2;
+ private ConnectPoint cp3;
+ private ConnectPoint cp4;
+ private ConnectPoint cp5;
+ private ConnectPoint cp6;
+ private VirtualLink link1;
+ private VirtualLink link2;
+ private VirtualLink link3;
+ private VirtualLink link4;
+ private VirtualLink link5;
+ private VirtualLink link6;
+
+ private VirtualNetworkManager manager;
+ private static DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private VirtualNetworkIntentStore intentStore;
+ private CoreService coreService;
+ private TestableIntentService intentService = new FakeIntentManager();
+ private VirtualNetworkIntentManager vnetIntentService;
+ private TestIntentCompiler compiler = new TestIntentCompiler();
+ private IntentExtensionService intentExtensionService;
+ private WorkPartitionService workPartitionService;
+ private ServiceDirectory testDirectory;
+ private TestListener listener = new TestListener();
+ private static final int MAX_WAIT_TIME = 5;
+ private static final int MAX_PERMITS = 1;
+ private static Semaphore created;
+ private static Semaphore withdrawn;
+ private static Semaphore purged;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+ intentStore = new SimpleVirtualIntentStore();
+
+ coreService = new VirtualNetworkIntentManagerTest.TestCoreService();
+
+ MockIdGenerator.cleanBind();
+
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+ intentService.addListener(listener);
+
+ // Register a compiler and an installer both setup for success.
+ intentExtensionService = intentService;
+ intentExtensionService.registerCompiler(VirtualNetworkIntent.class, compiler);
+
+ created = new Semaphore(0, true);
+ withdrawn = new Semaphore(0, true);
+ purged = new Semaphore(0, true);
+
+ workPartitionService = new WorkPartitionServiceAdapter();
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(IntentService.class, intentService)
+ .add(WorkPartitionService.class, workPartitionService);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ MockIdGenerator.unbind();
+ intentService.removeListener(listener);
+ created = null;
+ withdrawn = null;
+ purged = null;
+ }
+
+ /**
+ * Method to create the virtual network for further testing.
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupVirtualNetworkTopology() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ VirtualDevice virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID3);
+ VirtualDevice virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID4);
+
+ Port port1 = new DefaultPort(virtualDevice1, PortNumber.portNumber(1), true);
+ cp1 = new ConnectPoint(virtualDevice1.id(), port1.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(), port1.number(), cp1);
+
+ Port port2 = new DefaultPort(virtualDevice1, PortNumber.portNumber(2), true);
+ cp2 = new ConnectPoint(virtualDevice1.id(), port2.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(), port2.number(), cp2);
+
+ Port port3 = new DefaultPort(virtualDevice2, PortNumber.portNumber(3), true);
+ cp3 = new ConnectPoint(virtualDevice2.id(), port3.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(), port3.number(), cp3);
+
+ Port port4 = new DefaultPort(virtualDevice2, PortNumber.portNumber(4), true);
+ cp4 = new ConnectPoint(virtualDevice2.id(), port4.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(), port4.number(), cp4);
+
+ Port port5 = new DefaultPort(virtualDevice3, PortNumber.portNumber(5), true);
+ cp5 = new ConnectPoint(virtualDevice3.id(), port5.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(), port5.number(), cp5);
+
+ Port port6 = new DefaultPort(virtualDevice3, PortNumber.portNumber(6), true);
+ cp6 = new ConnectPoint(virtualDevice3.id(), port6.number());
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(), port6.number(), cp6);
+
+ link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+
+ vnetIntentService = new VirtualNetworkIntentManager(manager, virtualNetwork.id());
+ vnetIntentService.intentStore = intentStore;
+ return virtualNetwork;
+ }
+
+ /**
+ * Tests the submit(), withdraw(), and purge() methods.
+ */
+ @Test
+ public void testCreateAndRemoveIntent() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ Key intentKey = Key.of("test", APP_ID);
+
+ List<Constraint> constraints = new ArrayList<>();
+ constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
+
+ VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
+ .networkId(virtualNetwork.id())
+ .key(intentKey)
+ .appId(APP_ID)
+ .ingressPoint(cp1)
+ .egressPoint(cp5)
+ .constraints(constraints)
+ .build();
+ // Test the submit() method.
+ vnetIntentService.submit(virtualIntent);
+
+ // Wait for the both intents to go into an INSTALLED state.
+ try {
+ if (!created.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for intent to get installed.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception during intent installation." + e.getMessage());
+ }
+
+ // Test the getIntentState() method
+ assertEquals("The intent state did not match as expected.", IntentState.INSTALLED,
+ vnetIntentService.getIntentState(virtualIntent.key()));
+
+ // Test the withdraw() method.
+ vnetIntentService.withdraw(virtualIntent);
+ // Wait for the both intents to go into a WITHDRAWN state.
+ try {
+ if (!withdrawn.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for intent to get withdrawn.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception during intent withdrawal." + e.getMessage());
+ }
+
+ // Test the getIntentState() method
+ assertEquals("The intent state did not match as expected.", IntentState.WITHDRAWN,
+ vnetIntentService.getIntentState(virtualIntent.key()));
+
+ // Test the purge() method.
+ vnetIntentService.purge(virtualIntent);
+ // Wait for the both intents to be removed/purged.
+ try {
+ if (!purged.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for intent to get purged.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception during intent purging." + e.getMessage());
+ }
+
+ }
+
+ /**
+ * Tests the getIntents, getIntent(), getIntentData(), getIntentCount(),
+ * isLocal() methods.
+ */
+ @Test
+ public void testGetIntents() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ Key intentKey = Key.of("test", APP_ID);
+
+ List<Constraint> constraints = new ArrayList<>();
+ constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
+
+ VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
+ .networkId(virtualNetwork.id())
+ .key(intentKey)
+ .appId(APP_ID)
+ .ingressPoint(cp1)
+ .egressPoint(cp5)
+ .constraints(constraints)
+ .build();
+ // Test the submit() method.
+ vnetIntentService.submit(virtualIntent);
+
+ // Wait for the both intents to go into an INSTALLED state.
+ try {
+ if (!created.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for intent to get installed.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception during intent installation." + e.getMessage());
+ }
+
+ // Test the getIntents() method
+ assertEquals("The intents size did not match as expected.", 1,
+ Iterators.size(vnetIntentService.getIntents().iterator()));
+
+ // Test the getIntent() method
+ assertNotNull("The intent should have been found.", vnetIntentService.getIntent(virtualIntent.key()));
+
+ // Test the getIntentData() method
+ assertEquals("The intent data size did not match as expected.", 1,
+ Iterators.size(vnetIntentService.getIntentData().iterator()));
+
+ // Test the getIntentCount() method
+ assertEquals("The intent count did not match as expected.", 1,
+ vnetIntentService.getIntentCount());
+
+ // Test the isLocal() method
+ assertTrue("The intent should be local.", vnetIntentService.isLocal(virtualIntent.key()));
+
+ }
+
+ /**
+ * Test listener to listen for intent events.
+ */
+ private static class TestListener implements IntentListener {
+
+ @Override
+ public void event(IntentEvent event) {
+ switch (event.type()) {
+ case INSTALLED:
+ // Release one permit on the created semaphore since the Intent event was received.
+// virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.INSTALLED);
+ created.release();
+ break;
+ case WITHDRAWN:
+ // Release one permit on the removed semaphore since the Intent event was received.
+// virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.WITHDRAWN);
+ withdrawn.release();
+ break;
+ case PURGED:
+ // Release one permit on the purged semaphore since the Intent event was received.
+ purged.release();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+ }
+
+ private static class TestIntentCompiler implements IntentCompiler<VirtualNetworkIntent> {
+ @Override
+ public List<Intent> compile(VirtualNetworkIntent intent, List<Intent> installable) {
+ return Lists.newArrayList(new MockInstallableIntent());
+ }
+ }
+
+ private static class MockInstallableIntent extends FlowRuleIntent {
+
+ public MockInstallableIntent() {
+ super(APP_ID, null, Collections.singletonList(new IntentTestsMocks.MockFlowRule(100)),
+ Collections.emptyList(), PathIntent.ProtectionType.PRIMARY, null);
+ }
+ }
+
+// private void addOrUpdateIntent(Intent intent, IntentState state) {
+// checkNotNull(intent, "Intent cannot be null");
+// IntentData intentData = intentStore.(intent.key());
+// if (intentData == null) {
+// intentData = new IntentData(intent, state, new WallClockTimestamp(System.currentTimeMillis()));
+// } else {
+// intentData = new IntentData(intent, state, intentData.version());
+// }
+// intentKeyIntentDataMap.put(intent.key(), intentData);
+// }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManagerTest.java
new file mode 100644
index 0000000..2805f9e
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkLinkManagerTest.java
@@ -0,0 +1,326 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Iterators;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * Junit tests for VirtualNetworkLinkService.
+ */
+public class VirtualNetworkLinkManagerTest extends TestDeviceParams {
+
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService;
+ private TestServiceDirectory testDirectory;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ coreService = new VirtualNetworkLinkManagerTest.TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Tests the getLinks(), getActiveLinks(), getLinkCount(), getLink(),
+ * getLinks(ConnectPoint), getDeviceLinks(), getDeviceEgressLinks(), getDeviceIngressLinks(),
+ * getEgressLinks(), getIngressLinks() methods.
+ */
+ @Test
+ public void testGetLinks() {
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), src.deviceId(), src.port(),
+ new ConnectPoint(srcVirtualDevice.id(), src.port()));
+
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), dst.deviceId(), dst.port(),
+ new ConnectPoint(dstVirtualDevice.id(), dst.port()));
+
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), src, dst);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), dst, src);
+
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getLinks() method
+ Iterator<Link> it = linkService.getLinks().iterator();
+ assertEquals("The link set size did not match.", 2, Iterators.size(it));
+
+ // test the getActiveLinks() method where all links are INACTIVE
+ Iterator<Link> it2 = linkService.getActiveLinks().iterator();
+ assertEquals("The link set size did not match.", 0, Iterators.size(it2));
+
+ // test the getActiveLinks() method where one link is ACTIVE
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ Iterator<Link> it3 = linkService.getActiveLinks().iterator();
+ assertEquals("The link set size did not match.", 1, Iterators.size(it3));
+
+ // test the getLinkCount() method
+ assertEquals("The link set size did not match.", 2, linkService.getLinkCount());
+
+ // test the getLink() method
+ assertEquals("The expect link did not match.", link1,
+ linkService.getLink(src, dst));
+ assertEquals("The expect link did not match.", link2,
+ linkService.getLink(dst, src));
+ assertNotEquals("The expect link should not have matched.", link1,
+ linkService.getLink(dst, src));
+
+ // test the getLinks(ConnectPoint) method
+ assertEquals("The link set size did not match.", 2, linkService.getLinks(src).size());
+ assertEquals("The link set size did not match.", 2, linkService.getLinks(dst).size());
+ ConnectPoint connectPoint = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(3));
+ assertEquals("The link set size did not match.", 0, linkService.getLinks(connectPoint).size());
+
+ // test the getDeviceLinks() method
+ assertEquals("The link set size did not match.", 2,
+ linkService.getDeviceLinks(DID1).size());
+ assertEquals("The link set size did not match.", 2,
+ linkService.getDeviceLinks(DID2).size());
+ assertEquals("The link set size did not match.", 0,
+ linkService.getDeviceLinks(DID3).size());
+
+ // test the getDeviceEgressLinks() method
+ assertEquals("The link set size did not match.", 1,
+ linkService.getDeviceEgressLinks(DID1).size());
+ assertEquals("The link set size did not match.", 1,
+ linkService.getDeviceEgressLinks(DID2).size());
+ assertEquals("The link set size did not match.", 0,
+ linkService.getDeviceEgressLinks(DID3).size());
+
+ // test the getDeviceIngressLinks() method
+ assertEquals("The link set size did not match.", 1,
+ linkService.getDeviceIngressLinks(DID1).size());
+ assertEquals("The link set size did not match.", 1,
+ linkService.getDeviceIngressLinks(DID2).size());
+ assertEquals("The link set size did not match.", 0,
+ linkService.getDeviceIngressLinks(DID3).size());
+
+ // test the getEgressLinks() method
+ assertEquals("The link set size did not match.", 1,
+ linkService.getEgressLinks(src).size());
+ assertEquals("The link set size did not match.", 1,
+ linkService.getEgressLinks(dst).size());
+ assertEquals("The link set size did not match.", 0,
+ linkService.getEgressLinks(connectPoint).size());
+
+ // test the getIngressLinks() method
+ assertEquals("The link set size did not match.", 1,
+ linkService.getIngressLinks(src).size());
+ assertEquals("The link set size did not match.", 1,
+ linkService.getIngressLinks(dst).size());
+ assertEquals("The link set size did not match.", 0,
+ linkService.getIngressLinks(connectPoint).size());
+ }
+
+ /**
+ * Tests the getLink() method using a null src connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetLinkByNullSrc() {
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualLink(virtualNetwork.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork.id(), dst, src);
+
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getLink() method with a null src connect point.
+ linkService.getLink(null, dst);
+ }
+
+ /**
+ * Tests the getLink() method using a null dst connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetLinkByNullDst() {
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualLink(virtualNetwork.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork.id(), dst, src);
+
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getLink() method with a null dst connect point.
+ linkService.getLink(src, null);
+ }
+
+ /**
+ * Tests querying for links using a null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDeviceLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getDeviceLinks() method with a null device identifier.
+ linkService.getDeviceLinks(null);
+ }
+
+ /**
+ * Tests querying for links using a null connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getLinks() method with a null connect point.
+ linkService.getLinks(null);
+ }
+
+ /**
+ * Tests querying for device egress links using a null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDeviceEgressLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getDeviceEgressLinks() method with a null device identifier.
+ linkService.getDeviceEgressLinks(null);
+ }
+
+ /**
+ * Tests querying for device ingress links using a null device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDeviceIngressLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getDeviceIngressLinks() method with a null device identifier.
+ linkService.getDeviceIngressLinks(null);
+ }
+
+ /**
+ * Tests querying for egress links using a null connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetEgressLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getEgressLinks() method with a null connect point.
+ linkService.getEgressLinks(null);
+ }
+
+ /**
+ * Tests querying for ingress links using a null connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetIngressLinksByNullId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ LinkService linkService = manager.get(virtualNetwork.id(), LinkService.class);
+
+ // test the getIngressLinks() method with a null connect point.
+ linkService.getIngressLinks(null);
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
new file mode 100644
index 0000000..9156c38
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -0,0 +1,1003 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
+import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.impl.provider.DefaultVirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.impl.provider.DefaultVirtualGroupProvider;
+import org.onosproject.incubator.net.virtual.impl.provider.DefaultVirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.impl.provider.DefaultVirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowRuleStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualGroupStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualIntentStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualPacketStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Junit tests for VirtualNetworkManager.
+ */
+public class VirtualNetworkManagerTest extends VirtualNetworkTestUtil {
+ private final String tenantIdValue1 = "TENANT_ID1";
+ private final String tenantIdValue2 = "TENANT_ID2";
+
+ private VirtualNetworkManager manager;
+ private DefaultVirtualNetworkProvider topologyProvider;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService;
+ private TestListener listener = new TestListener();
+ private TopologyService topologyService;
+
+ private ConnectPoint cp6;
+ private ConnectPoint cp7;
+
+ private TestServiceDirectory testDirectory;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+ MockIdGenerator.cleanBind();
+
+ coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService",
+ new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.addListener(listener);
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.removeListener(listener);
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ MockIdGenerator.cleanBind();
+ }
+
+ /**
+ * Tests registering a null tenant id.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testRegisterNullTenantId() {
+ manager.registerTenantId(null);
+ }
+
+ /**
+ * Tests registering/unregistering a tenant id.
+ */
+ @Test
+ public void testRegisterUnregisterTenantId() {
+ manager.unregisterTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue2));
+ Collection<TenantId> tenantIdCollection = manager.getTenantIds();
+ assertEquals("The tenantId set size did not match.", 2, tenantIdCollection.size());
+
+ manager.unregisterTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.unregisterTenantId(TenantId.tenantId(tenantIdValue2));
+ tenantIdCollection = manager.getTenantIds();
+ assertTrue("The tenantId set should be empty.", tenantIdCollection.isEmpty());
+
+ // Validate that the events were all received in the correct order.
+ validateEvents(VirtualNetworkEvent.Type.TENANT_REGISTERED,
+ VirtualNetworkEvent.Type.TENANT_REGISTERED,
+ VirtualNetworkEvent.Type.TENANT_UNREGISTERED,
+ VirtualNetworkEvent.Type.TENANT_UNREGISTERED);
+ }
+
+ /**
+ * Test method {@code getTenantId()} for registered virtual network.
+ */
+ @Test
+ public void testGetTenantIdForRegisteredVirtualNetwork() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology(tenantIdValue1);
+ TenantId tenantId = manager.getTenantId(virtualNetwork.id());
+
+ assertThat(tenantId.toString(), is(tenantIdValue1));
+ }
+
+ /**
+ * Test method {@code getTenantId()} for null virtual network id.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetTenantIdForNullVirtualNetwork() {
+ manager.getTenantId(null);
+ }
+
+ /**
+ * Test method {@code getVirtualNetwork()} for registered virtual network.
+ */
+ @Test
+ public void testGetVirtualNetworkForRegisteredNetwork() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology(tenantIdValue1);
+
+ assertNotNull("Registered virtual network is null", manager.getVirtualNetwork(virtualNetwork.id()));
+ }
+
+ /**
+ * Test method {@code getVirtualNetwork()} for null virtual network id.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetVirtualForNullVirtualNetworkId() {
+ manager.getVirtualNetwork(null);
+ }
+
+ /**
+ * Tests adding a null virtual network.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testCreateNullVirtualNetwork() {
+ manager.createVirtualNetwork(null);
+ }
+
+ /**
+ * Tests removal of a virtual network twice.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testRemoveVnetTwice() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ manager.removeVirtualNetwork(virtualNetwork.id());
+ manager.removeVirtualNetwork(virtualNetwork.id());
+ }
+
+ /**
+ * Tests add and remove of virtual networks.
+ */
+ @Test
+ public void testAddRemoveVirtualNetwork() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ Set<VirtualNetwork> virtualNetworks = manager.getVirtualNetworks(TenantId.tenantId(tenantIdValue1));
+ assertNotNull("The virtual network set should not be null", virtualNetworks);
+ assertEquals("The virtual network set size did not match.", 2, virtualNetworks.size());
+
+ int remaining = virtualNetworks.size();
+ for (VirtualNetwork virtualNetwork : virtualNetworks) {
+ manager.removeVirtualNetwork(virtualNetwork.id());
+ assertEquals("The expected virtual network size does not match",
+ --remaining, manager.getVirtualNetworks(TenantId.tenantId(tenantIdValue1)).size());
+ }
+ virtualNetworks = manager.getVirtualNetworks(TenantId.tenantId(tenantIdValue1));
+ assertTrue("The virtual network set should be empty.", virtualNetworks.isEmpty());
+
+ // Create/remove a virtual network.
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ manager.removeVirtualNetwork(virtualNetwork.id());
+
+ virtualNetworks = manager.getVirtualNetworks(TenantId.tenantId(tenantIdValue1));
+ assertTrue("The virtual network set should be empty.", virtualNetworks.isEmpty());
+
+ // Validate that the events were all received in the correct order.
+ validateEvents(VirtualNetworkEvent.Type.TENANT_REGISTERED,
+ VirtualNetworkEvent.Type.NETWORK_ADDED,
+ VirtualNetworkEvent.Type.NETWORK_ADDED,
+ VirtualNetworkEvent.Type.NETWORK_REMOVED,
+ VirtualNetworkEvent.Type.NETWORK_REMOVED,
+ VirtualNetworkEvent.Type.NETWORK_ADDED,
+ VirtualNetworkEvent.Type.NETWORK_REMOVED);
+ }
+
+ /**
+ * Tests adding a null virtual device.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testCreateNullVirtualDevice() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ manager.createVirtualDevice(virtualNetwork.id(), null);
+ }
+
+ /**
+ * Tests adding a virtual device where no virtual network exists.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testCreateVirtualDeviceWithNoNetwork() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork =
+ new DefaultVirtualNetwork(NetworkId.NONE,
+ TenantId.tenantId(tenantIdValue1));
+
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ }
+
+ /**
+ * Tests add and remove of virtual devices.
+ */
+ @Test
+ public void testAddRemoveVirtualDevice() {
+ List<VirtualNetworkEvent.Type> expectedEventTypes = new ArrayList<>();
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ expectedEventTypes.add(VirtualNetworkEvent.Type.TENANT_REGISTERED);
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ expectedEventTypes.add(VirtualNetworkEvent.Type.NETWORK_ADDED);
+ VirtualNetwork virtualNetwork2 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ expectedEventTypes.add(VirtualNetworkEvent.Type.NETWORK_ADDED);
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
+ manager.createVirtualDevice(virtualNetwork2.id(), DID2);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
+
+ Set<VirtualDevice> virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
+ assertNotNull("The virtual device set should not be null", virtualDevices1);
+ assertEquals("The virtual device set size did not match.", 1, virtualDevices1.size());
+
+ Set<VirtualDevice> virtualDevices2 = manager.getVirtualDevices(virtualNetwork2.id());
+ assertNotNull("The virtual device set should not be null", virtualDevices2);
+ assertEquals("The virtual device set size did not match.", 1, virtualDevices2.size());
+
+ for (VirtualDevice virtualDevice : virtualDevices1) {
+ manager.removeVirtualDevice(virtualNetwork1.id(), virtualDevice.id());
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED);
+ // attempt to remove the same virtual device again - no event expected.
+ manager.removeVirtualDevice(virtualNetwork1.id(), virtualDevice.id());
+ }
+ virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
+ assertTrue("The virtual device set should be empty.", virtualDevices1.isEmpty());
+
+ // Add/remove the virtual device again.
+ VirtualDevice virtualDevice = manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
+ manager.removeVirtualDevice(virtualDevice.networkId(), virtualDevice.id());
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_REMOVED);
+ virtualDevices1 = manager.getVirtualDevices(virtualNetwork1.id());
+ assertTrue("The virtual device set should be empty.", virtualDevices1.isEmpty());
+
+ // Validate that the events were all received in the correct order.
+ validateEvents(expectedEventTypes.toArray(
+ new VirtualNetworkEvent.Type[expectedEventTypes.size()]));
+ }
+
+ /**
+ * Tests getting a collection of physical device identifier corresponding to
+ * the specified virtual device.
+ */
+ @Test
+ public void testGetPhysicalDevices() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue2));
+
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork2 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue2));
+
+ // two virtual device in first virtual network
+ VirtualDevice vDevice1InVnet1 =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ VirtualDevice vDevice2InVnet1 =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID2);
+ // Two virtual device in second virtual network
+ VirtualDevice vDevice1InVnet2 =
+ manager.createVirtualDevice(virtualNetwork2.id(), DID1);
+ VirtualDevice vDevice2InVnet2 =
+ manager.createVirtualDevice(virtualNetwork2.id(), DID2);
+
+ // Connection Point from each physical device
+ // Virtual network 1
+ ConnectPoint cp1InVnet1 =
+ new ConnectPoint(PHYDID1, PortNumber.portNumber(10));
+ ConnectPoint cp2InVnet1 =
+ new ConnectPoint(PHYDID2, PortNumber.portNumber(20));
+ ConnectPoint cp3InVnet1 =
+ new ConnectPoint(PHYDID3, PortNumber.portNumber(30));
+ ConnectPoint cp4InVnet1 =
+ new ConnectPoint(PHYDID4, PortNumber.portNumber(40));
+ // Virtual network 2
+ ConnectPoint cp1InVnet2 =
+ new ConnectPoint(PHYDID1, PortNumber.portNumber(10));
+ ConnectPoint cp2InVnet2 =
+ new ConnectPoint(PHYDID2, PortNumber.portNumber(20));
+ ConnectPoint cp3InVnet2 =
+ new ConnectPoint(PHYDID3, PortNumber.portNumber(30));
+ ConnectPoint cp4InVnet2 =
+ new ConnectPoint(PHYDID4, PortNumber.portNumber(40));
+
+ // Make simple BigSwitch by mapping two phyDevice to one vDevice
+ // First vDevice in first virtual network
+ manager.createVirtualPort(virtualNetwork1.id(),
+ vDevice1InVnet1.id(), PortNumber.portNumber(1), cp1InVnet1);
+ manager.createVirtualPort(virtualNetwork1.id(),
+ vDevice1InVnet1.id(), PortNumber.portNumber(2), cp2InVnet1);
+ // Second vDevice in first virtual network
+ manager.createVirtualPort(virtualNetwork1.id(),
+ vDevice2InVnet1.id(), PortNumber.portNumber(1), cp3InVnet1);
+ manager.createVirtualPort(virtualNetwork1.id(),
+ vDevice2InVnet1.id(), PortNumber.portNumber(2), cp4InVnet1);
+ // First vDevice in second virtual network
+ manager.createVirtualPort(virtualNetwork2.id(),
+ vDevice1InVnet2.id(), PortNumber.portNumber(1), cp1InVnet2);
+ manager.createVirtualPort(virtualNetwork2.id(),
+ vDevice1InVnet2.id(), PortNumber.portNumber(2), cp2InVnet2);
+ // Second vDevice in second virtual network
+ manager.createVirtualPort(virtualNetwork2.id(),
+ vDevice2InVnet2.id(), PortNumber.portNumber(1), cp3InVnet2);
+ manager.createVirtualPort(virtualNetwork2.id(),
+ vDevice2InVnet2.id(), PortNumber.portNumber(2), cp4InVnet2);
+
+
+ Set<DeviceId> physicalDeviceSet;
+ Set<DeviceId> testSet = new HashSet<>();
+ physicalDeviceSet = manager.getPhysicalDevices(virtualNetwork1.id(), vDevice1InVnet1.id());
+ testSet.add(PHYDID1);
+ testSet.add(PHYDID2);
+ assertEquals("The physical devices 1 did not match", testSet, physicalDeviceSet);
+ testSet.clear();
+
+ physicalDeviceSet = manager.getPhysicalDevices(virtualNetwork1.id(), vDevice2InVnet1.id());
+ testSet.add(PHYDID3);
+ testSet.add(PHYDID4);
+ assertEquals("The physical devices 2 did not match", testSet, physicalDeviceSet);
+ testSet.clear();
+
+ physicalDeviceSet = manager.getPhysicalDevices(virtualNetwork2.id(), vDevice1InVnet2.id());
+ testSet.add(PHYDID1);
+ testSet.add(PHYDID2);
+ assertEquals("The physical devices 1 did not match", testSet, physicalDeviceSet);
+ testSet.clear();
+
+ physicalDeviceSet = manager.getPhysicalDevices(virtualNetwork2.id(), vDevice2InVnet2.id());
+ testSet.add(PHYDID3);
+ testSet.add(PHYDID4);
+ assertEquals("The physical devices 2 did not match", testSet, physicalDeviceSet);
+ testSet.clear();
+ }
+
+ /**
+ * Tests adding a null virtual host.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testCreateNullVirtualHost() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ manager.createVirtualHost(virtualNetwork.id(), null, null, null, null, null);
+ }
+
+ /**
+ * Tests adding a virtual host where no virtual network exists.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testCreateVirtualHostWithNoNetwork() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork =
+ new DefaultVirtualNetwork(NetworkId.NONE, TenantId.tenantId(tenantIdValue1));
+
+ manager.createVirtualHost(virtualNetwork.id(), HID1, null, null, null, null);
+ }
+
+ /**
+ * Tests adding a virtual host where no virtual port exists.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testCreateVirtualHostWithNoVirtualPort() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ manager.createVirtualHost(virtualNetwork1.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ }
+
+ /**
+ * Tests add and remove of virtual hosts.
+ */
+ @Test
+ public void testAddRemoveVirtualHost() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork2 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork2.id(), DID2);
+
+ ConnectPoint hostCp1 = new ConnectPoint(DID1, P1);
+ ConnectPoint hostCp2 = new ConnectPoint(DID2, P2);
+ manager.createVirtualPort(virtualNetwork1.id(), hostCp1.deviceId(), hostCp1.port(),
+ new ConnectPoint(virtualDevice1.id(), hostCp1.port()));
+ manager.createVirtualPort(virtualNetwork2.id(), hostCp2.deviceId(), hostCp2.port(),
+ new ConnectPoint(virtualDevice2.id(), hostCp2.port()));
+
+ manager.createVirtualHost(virtualNetwork1.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+ manager.createVirtualHost(virtualNetwork2.id(), HID2, MAC2, VLAN2, LOC2, IPSET2);
+
+ Set<VirtualHost> virtualHosts1 = manager.getVirtualHosts(virtualNetwork1.id());
+ assertNotNull("The virtual host set should not be null", virtualHosts1);
+ assertEquals("The virtual host set size did not match.", 1, virtualHosts1.size());
+
+ Set<VirtualHost> virtualHosts2 = manager.getVirtualHosts(virtualNetwork2.id());
+ assertNotNull("The virtual host set should not be null", virtualHosts2);
+ assertEquals("The virtual host set size did not match.", 1, virtualHosts2.size());
+
+ for (VirtualHost virtualHost : virtualHosts1) {
+ manager.removeVirtualHost(virtualNetwork1.id(), virtualHost.id());
+ // attempt to remove the same virtual host again.
+ manager.removeVirtualHost(virtualNetwork1.id(), virtualHost.id());
+ }
+ virtualHosts1 = manager.getVirtualHosts(virtualNetwork1.id());
+ assertTrue("The virtual host set should be empty.", virtualHosts1.isEmpty());
+
+ // Add/remove the virtual host again.
+ VirtualHost virtualHost =
+ manager.createVirtualHost(virtualNetwork1.id(),
+ HID1, MAC1, VLAN1, LOC1, IPSET1);
+ manager.removeVirtualHost(virtualHost.networkId(), virtualHost.id());
+ virtualHosts1 = manager.getVirtualHosts(virtualNetwork1.id());
+ assertTrue("The virtual host set should be empty.", virtualHosts1.isEmpty());
+ }
+
+ /**
+ * Tests add and remove of virtual links.
+ */
+ @Test
+ public void testAddRemoveVirtualLink() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID2);
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+ new ConnectPoint(srcVirtualDevice.id(), src.port()));
+
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork1.id(), dst.deviceId(), dst.port(),
+ new ConnectPoint(dstVirtualDevice.id(), dst.port()));
+
+ manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork1.id(), dst, src);
+
+ Set<VirtualLink> virtualLinks = manager.getVirtualLinks(virtualNetwork1.id());
+ assertNotNull("The virtual link set should not be null", virtualLinks);
+ assertEquals("The virtual link set size did not match.", 2, virtualLinks.size());
+
+ for (VirtualLink virtualLink : virtualLinks) {
+ manager.removeVirtualLink(virtualLink.networkId(), virtualLink.src(), virtualLink.dst());
+ // attempt to remove the same virtual link again.
+ manager.removeVirtualLink(virtualLink.networkId(), virtualLink.src(), virtualLink.dst());
+ }
+ virtualLinks = manager.getVirtualLinks(virtualNetwork1.id());
+ assertTrue("The virtual link set should be empty.", virtualLinks.isEmpty());
+
+ // Add/remove the virtual link again.
+ VirtualLink virtualLink = manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ manager.removeVirtualLink(virtualLink.networkId(), virtualLink.src(), virtualLink.dst());
+ virtualLinks = manager.getVirtualLinks(virtualNetwork1.id());
+ assertTrue("The virtual link set should be empty.", virtualLinks.isEmpty());
+ }
+
+ /**
+ * Tests adding the same virtual link twice.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testAddSameVirtualLink() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID2);
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+ new ConnectPoint(srcVirtualDevice.id(), src.port()));
+
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork1.id(), dst.deviceId(), dst.port(),
+ new ConnectPoint(dstVirtualDevice.id(), dst.port()));
+
+ manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ }
+
+ private VirtualPort getPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+ Set<VirtualPort> virtualPorts = manager.getVirtualPorts(networkId, deviceId);
+ return virtualPorts.stream().filter(virtualPort -> virtualPort.number().equals(portNumber))
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Tests add, bind and remove of virtual ports.
+ */
+ @Test
+ public void testAddRemoveVirtualPort() {
+ List<VirtualNetworkEvent.Type> expectedEventTypes = new ArrayList<>();
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ expectedEventTypes.add(VirtualNetworkEvent.Type.TENANT_REGISTERED);
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ expectedEventTypes.add(VirtualNetworkEvent.Type.NETWORK_ADDED);
+ VirtualDevice virtualDevice =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_DEVICE_ADDED);
+ ConnectPoint cp = new ConnectPoint(virtualDevice.id(), PortNumber.portNumber(1));
+
+ manager.createVirtualPort(virtualNetwork1.id(),
+ virtualDevice.id(), PortNumber.portNumber(1), cp);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
+ manager.createVirtualPort(virtualNetwork1.id(),
+ virtualDevice.id(), PortNumber.portNumber(2), cp);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
+
+ Set<VirtualPort> virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), virtualDevice.id());
+ assertNotNull("The virtual port set should not be null", virtualPorts);
+ assertEquals("The virtual port set size did not match.", 2, virtualPorts.size());
+ virtualPorts.forEach(vp -> assertFalse("Initial virtual port state should be disabled", vp.isEnabled()));
+
+ // verify change state of virtual port (disabled -> enabled)
+ manager.updatePortState(virtualNetwork1.id(), virtualDevice.id(), PortNumber.portNumber(1), true);
+ VirtualPort changedPort = getPort(virtualNetwork1.id(), virtualDevice.id(), PortNumber.portNumber(1));
+ assertNotNull("The changed virtual port should not be null", changedPort);
+ assertEquals("Virtual port state should be enabled", true, changedPort.isEnabled());
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED);
+
+ // verify change state of virtual port (disabled -> disabled)
+ manager.updatePortState(virtualNetwork1.id(), virtualDevice.id(), PortNumber.portNumber(2), false);
+ changedPort = getPort(virtualNetwork1.id(), virtualDevice.id(), PortNumber.portNumber(2));
+ assertNotNull("The changed virtual port should not be null", changedPort);
+ assertEquals("Virtual port state should be disabled", false, changedPort.isEnabled());
+ // no VIRTUAL_PORT_UPDATED event is expected - the requested state (disabled) is same as previous state.
+
+ for (VirtualPort virtualPort : virtualPorts) {
+ manager.removeVirtualPort(virtualNetwork1.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED);
+ // attempt to remove the same virtual port again.
+ manager.removeVirtualPort(virtualNetwork1.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ }
+ virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), virtualDevice.id());
+ assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+
+ // Add/remove the virtual port again.
+ VirtualPort virtualPort =
+ manager.createVirtualPort(virtualNetwork1.id(), virtualDevice.id(),
+ PortNumber.portNumber(1), cp);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_ADDED);
+
+ ConnectPoint newCp = new ConnectPoint(DID2, PortNumber.portNumber(2));
+ manager.bindVirtualPort(virtualNetwork1.id(), virtualDevice.id(),
+ PortNumber.portNumber(1), newCp);
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_UPDATED);
+
+ manager.removeVirtualPort(virtualNetwork1.id(),
+ (DeviceId) virtualPort.element().id(), virtualPort.number());
+ expectedEventTypes.add(VirtualNetworkEvent.Type.VIRTUAL_PORT_REMOVED);
+ virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), virtualDevice.id());
+ assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+
+ // Validate that the events were all received in the correct order.
+ validateEvents(expectedEventTypes.toArray(
+ new VirtualNetworkEvent.Type[expectedEventTypes.size()]));
+ }
+
+ /**
+ * Tests when a virtual element is removed, all the other elements depending on it are also removed.
+ */
+ @Test
+ public void testRemoveAllElements() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork1 =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork1.id(), DID2);
+ ConnectPoint src = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+ new ConnectPoint(PHYDID1, PortNumber.portNumber(1)));
+
+ ConnectPoint dst = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork1.id(), dst.deviceId(), dst.port(),
+ new ConnectPoint(PHYDID2, PortNumber.portNumber(2)));
+
+ manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork1.id(), dst, src);
+
+ ConnectPoint hostCp = new ConnectPoint(DID1, P1);
+ manager.createVirtualPort(virtualNetwork1.id(), hostCp.deviceId(), hostCp.port(),
+ new ConnectPoint(PHYDID1, P1));
+ manager.createVirtualHost(virtualNetwork1.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+
+ //When a virtual port is removed, all virtual links connected to it should also be removed.
+ manager.removeVirtualPort(virtualNetwork1.id(), DID1, PortNumber.portNumber(1));
+ Set<VirtualLink> virtualLinks = manager.getVirtualLinks(virtualNetwork1.id());
+ assertTrue("The virtual link set should be empty.", virtualLinks.isEmpty());
+
+ //When a virtual port is removed, all virtual hosts located to it should also be removed.
+ manager.removeVirtualPort(virtualNetwork1.id(), DID1, P1);
+ Set<VirtualHost> virtualHosts = manager.getVirtualHosts(virtualNetwork1.id());
+ assertTrue("The virtual host set should be empty.", virtualHosts.isEmpty());
+
+ manager.createVirtualPort(virtualNetwork1.id(), src.deviceId(), src.port(),
+ new ConnectPoint(PHYDID1, PortNumber.portNumber(1)));
+ manager.createVirtualLink(virtualNetwork1.id(), src, dst);
+ manager.createVirtualLink(virtualNetwork1.id(), dst, src);
+ manager.createVirtualPort(virtualNetwork1.id(), hostCp.deviceId(), hostCp.port(),
+ new ConnectPoint(PHYDID1, P1));
+ manager.createVirtualHost(virtualNetwork1.id(), HID1, MAC1, VLAN1, LOC1, IPSET1);
+
+ //When a virtual device is removed, all virtual ports, hosts and links depended on it should also be removed.
+ manager.removeVirtualDevice(virtualNetwork1.id(), DID1);
+ Set<VirtualPort> virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), DID1);
+ assertTrue("The virtual port set of DID1 should be empty", virtualPorts.isEmpty());
+ virtualLinks = manager.getVirtualLinks(virtualNetwork1.id());
+ assertTrue("The virtual link set should be empty.", virtualLinks.isEmpty());
+ virtualHosts = manager.getVirtualHosts(virtualNetwork1.id());
+ assertTrue("The virtual host set should be empty.", virtualHosts.isEmpty());
+
+ //When a tenantId is removed, all the virtual networks belonging to it should also be removed.
+ manager.unregisterTenantId(TenantId.tenantId(tenantIdValue1));
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ Set<VirtualNetwork> virtualNetworks = manager.getVirtualNetworks(TenantId.tenantId(tenantIdValue1));
+ assertNotNull("The virtual network set should not be null", virtualNetworks);
+ assertTrue("The virtual network set should be empty.", virtualNetworks.isEmpty());
+ }
+
+ /**
+ * Tests the addTunnelId() method in the store with a null intent.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testAddTunnelIdNullIntent() {
+ manager.store.addTunnelId(null, null);
+ }
+
+ /**
+ * Tests the removeTunnelId() method in the store with a null intent.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testRemoveTunnelIdNullIntent() {
+ manager.store.removeTunnelId(null, null);
+ }
+
+ /**
+ * Tests the addTunnelId, getTunnelIds(), removeTunnelId() methods with the store.
+ */
+ @Test
+ public void testAddTunnelId() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ ConnectPoint cp1 = new ConnectPoint(DID1, P1);
+ ConnectPoint cp2 = new ConnectPoint(DID2, P1);
+
+ VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
+ .networkId(virtualNetwork.id())
+ .key(Key.of("Test", APP_ID))
+ .appId(APP_ID)
+ .ingressPoint(cp1)
+ .egressPoint(cp2)
+ .build();
+
+ TunnelId tunnelId = TunnelId.valueOf("virtual tunnel");
+ // Add the intent to tunnelID mapping to the store.
+ manager.store.addTunnelId(virtualIntent, tunnelId);
+ assertEquals("The tunnels size should match.", 1,
+ manager.store.getTunnelIds(virtualIntent).size());
+
+ // Remove the intent to tunnelID mapping from the store.
+ manager.store.removeTunnelId(virtualIntent, tunnelId);
+ assertTrue("The tunnels should be empty.",
+ manager.store.getTunnelIds(virtualIntent).isEmpty());
+ }
+
+
+ /**
+ * Method to create the virtual network for {@code tenantIdValue} for further testing.
+ **/
+ private VirtualNetwork setupVirtualNetworkTopology(String tenantIdValue) {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue));
+ VirtualNetwork virtualNetwork =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue));
+
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ VirtualDevice virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID3);
+ VirtualDevice virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID4);
+ VirtualDevice virtualDevice5 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID5);
+
+ ConnectPoint cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+ PortNumber.portNumber(1), cp1);
+
+ ConnectPoint cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+ PortNumber.portNumber(2), cp2);
+
+ ConnectPoint cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+ PortNumber.portNumber(3), cp3);
+
+ ConnectPoint cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+ PortNumber.portNumber(4), cp4);
+
+ ConnectPoint cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+ PortNumber.portNumber(5), cp5);
+
+ cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+ PortNumber.portNumber(6), cp6);
+
+ cp7 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(7));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+ PortNumber.portNumber(7), cp7);
+
+ ConnectPoint cp8 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(8));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+ PortNumber.portNumber(8), cp8);
+
+ ConnectPoint cp9 = new ConnectPoint(virtualDevice5.id(), PortNumber.portNumber(9));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice5.id(),
+ PortNumber.portNumber(9), cp9);
+
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp8, cp9);
+ virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp9, cp8);
+ virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+ topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ topologyProvider = new DefaultVirtualNetworkProvider();
+ try {
+ TestUtils.setField(topologyProvider, "topologyService", topologyService);
+ } catch (TestUtils.TestUtilsException e) {
+ e.printStackTrace();
+ }
+// topologyProvider.topologyService = topologyService;
+
+ return virtualNetwork;
+ }
+
+ /**
+ * Test the topologyChanged() method.
+ */
+ @Test
+ public void testTopologyChanged() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology(tenantIdValue1);
+ VirtualNetworkProviderService providerService =
+ manager.createProviderService(topologyProvider);
+
+ // Initial setup is two clusters of devices/links.
+ assertEquals("The cluster count did not match.", 2,
+ topologyService.currentTopology().clusterCount());
+
+ // Adding this link will join the two clusters together.
+ List<Event> reasons = new ArrayList<>();
+ VirtualLink link = manager.createVirtualLink(virtualNetwork.id(), cp6, cp7);
+ virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp7, cp6);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+
+ Topology topology = topologyService.currentTopology();
+ providerService.topologyChanged(topologyProvider.getConnectPoints(topology));
+
+ // Validate that all links are still active.
+ manager.getVirtualLinks(virtualNetwork.id()).forEach(virtualLink -> {
+ assertTrue("The virtual link should be active.",
+ virtualLink.state().equals(Link.State.ACTIVE));
+ });
+
+ virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.INACTIVE);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.INACTIVE);
+ providerService.topologyChanged(topologyProvider.getConnectPoints(topology));
+
+ // Validate that all links are active again.
+ manager.getVirtualLinks(virtualNetwork.id()).forEach(virtualLink -> {
+ assertTrue("The virtual link should be active.",
+ virtualLink.state().equals(Link.State.ACTIVE));
+ });
+ }
+
+ /**
+ * Tests that the get() method returns saved service instances.
+ */
+ @Test
+ public void testServiceGetReturnsSavedInstance() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork =
+ manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), DeviceService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), LinkService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), TopologyService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), HostService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), PathService.class);
+
+ // extra setup needed for FlowRuleService, PacketService, GroupService, and IntentService
+ VirtualProviderManager virtualProviderManager = new VirtualProviderManager();
+ virtualProviderManager.registerProvider(new DefaultVirtualFlowRuleProvider());
+ virtualProviderManager.registerProvider(new DefaultVirtualPacketProvider());
+ virtualProviderManager.registerProvider(new DefaultVirtualGroupProvider());
+ testDirectory.add(CoreService.class, coreService)
+ .add(VirtualProviderRegistryService.class, virtualProviderManager)
+ .add(EventDeliveryService.class, new TestEventDispatcher())
+ .add(ClusterService.class, new ClusterServiceAdapter())
+ .add(VirtualNetworkFlowRuleStore.class, new SimpleVirtualFlowRuleStore())
+ .add(VirtualNetworkPacketStore.class, new SimpleVirtualPacketStore())
+ .add(VirtualNetworkGroupStore.class, new SimpleVirtualGroupStore())
+ .add(VirtualNetworkIntentStore.class, new SimpleVirtualIntentStore())
+ .add(VirtualNetworkFlowObjectiveStore.class, new SimpleVirtualFlowObjectiveStore());
+
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), FlowRuleService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), FlowObjectiveService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), PacketService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), GroupService.class);
+ validateServiceGetReturnsSavedInstance(virtualNetwork.id(), IntentService.class);
+ }
+
+ /**
+ * Validates that the get() method returns saved service instances.
+ */
+ private <T> void validateServiceGetReturnsSavedInstance(NetworkId networkId,
+ Class<T> serviceClass) {
+ T serviceInstanceFirst = manager.get(networkId, serviceClass);
+ T serviceInstanceSubsequent = manager.get(networkId, serviceClass);
+ assertSame(serviceClass.getSimpleName() +
+ ": Subsequent get should be same as the first one",
+ serviceInstanceFirst, serviceInstanceSubsequent);
+ }
+
+ /**
+ * Method to validate that the actual versus expected virtual network events were
+ * received correctly.
+ *
+ * @param types expected virtual network events.
+ */
+ private void validateEvents(Enum... types) {
+ TestTools.assertAfter(100, () -> {
+ int i = 0;
+ assertEquals("wrong events received", types.length, listener.events.size());
+ for (Event event : listener.events) {
+ assertEquals("incorrect event type", types[i], event.type());
+ i++;
+ }
+ listener.events.clear();
+ });
+ }
+
+ /**
+ * Test listener class to receive virtual network events.
+ */
+ private static class TestListener implements VirtualNetworkListener {
+
+ private List<VirtualNetworkEvent> events = Lists.newArrayList();
+
+ @Override
+ public void event(VirtualNetworkEvent event) {
+ events.add(event);
+ }
+
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManagerTest.java
new file mode 100644
index 0000000..a08ab5c
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManagerTest.java
@@ -0,0 +1,312 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMastershipStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualMastershipStore;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.net.MastershipRole.STANDBY;
+import static org.onosproject.net.MastershipRole.NONE;
+
+public class VirtualNetworkMastershipManagerTest {
+
+ private static final NodeId NID_LOCAL = new NodeId("local");
+ private static final NodeId NID_OTHER = new NodeId("foo");
+ private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
+
+ private static final TenantId TID = TenantId.tenantId("1");
+
+ private static final DeviceId VDID1 = DeviceId.deviceId("foo:vd1");
+ private static final DeviceId VDID2 = DeviceId.deviceId("foo:vd2");
+ private static final DeviceId VDID3 = DeviceId.deviceId("foo:vd3");
+ private static final DeviceId VDID4 = DeviceId.deviceId("foo:vd4");
+
+ private static final NodeId NID1 = NodeId.nodeId("n1");
+ private static final NodeId NID2 = NodeId.nodeId("n2");
+ private static final NodeId NID3 = NodeId.nodeId("n3");
+ private static final NodeId NID4 = NodeId.nodeId("n4");
+
+ private static final ControllerNode CNODE1 =
+ new DefaultControllerNode(NID1, IpAddress.valueOf("127.0.1.1"));
+ private static final ControllerNode CNODE2 =
+ new DefaultControllerNode(NID2, IpAddress.valueOf("127.0.1.2"));
+ private static final ControllerNode CNODE3 =
+ new DefaultControllerNode(NID3, IpAddress.valueOf("127.0.1.3"));
+ private static final ControllerNode CNODE4 =
+ new DefaultControllerNode(NID4, IpAddress.valueOf("127.0.1.4"));
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+
+ private VirtualNetworkMastershipManager mastershipMgr1;
+ private VirtualNetworkMastershipManager mastershipMgr2;
+ protected MastershipService service;
+ private TestClusterService testClusterService;
+ private EventDeliveryService eventDeliveryService;
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ TestUtils.setField(manager, "coreService", coreService);
+
+ eventDeliveryService = new TestEventDispatcher();
+ NetTestTools.injectEventDispatcher(manager, eventDeliveryService);
+
+ SimpleVirtualMastershipStore store = new SimpleVirtualMastershipStore();
+ TestUtils.setField(store, "coreService", coreService);
+ store.activate();
+
+ testClusterService = new TestClusterService();
+
+ ServiceDirectory testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(ClusterService.class, testClusterService)
+ .add(VirtualNetworkMastershipStore.class, store);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ createVnets();
+
+ mastershipMgr1 = new VirtualNetworkMastershipManager(manager, vnet1.id());
+ mastershipMgr2 = new VirtualNetworkMastershipManager(manager, vnet2.id());
+ service = mastershipMgr1;
+ }
+
+ private void createVnets() {
+ manager.registerTenantId(TID);
+
+ vnet1 = manager.createVirtualNetwork(TID);
+ manager.createVirtualDevice(vnet1.id(), VDID1);
+ manager.createVirtualDevice(vnet1.id(), VDID2);
+
+ vnet2 = manager.createVirtualNetwork(TID);
+ manager.createVirtualDevice(vnet2.id(), VDID3);
+ manager.createVirtualDevice(vnet2.id(), VDID4);
+ }
+
+ @After
+ public void tearDown() {
+ manager.deactivate();
+ virtualNetworkManagerStore.deactivate();
+ }
+
+ @Test
+ public void setRole() {
+ mastershipMgr1.setRole(NID_OTHER, VDID1, MASTER);
+ assertEquals("wrong local role:", NONE, mastershipMgr1.getLocalRole(VDID1));
+ assertEquals("wrong obtained role:", STANDBY, Futures.getUnchecked(mastershipMgr1.requestRoleFor(VDID1)));
+
+ //set to master
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ assertEquals("wrong local role:", MASTER, mastershipMgr1.getLocalRole(VDID1));
+ }
+
+ @Test
+ public void relinquishMastership() {
+ //no backups - should just turn to NONE for device.
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ assertEquals("wrong role:", MASTER, mastershipMgr1.getLocalRole(VDID1));
+ mastershipMgr1.relinquishMastership(VDID1);
+ assertNull("wrong master:", mastershipMgr1.getMasterFor(VDID2));
+ assertEquals("wrong role:", NONE, mastershipMgr1.getLocalRole(VDID1));
+
+ //not master, nothing should happen
+ mastershipMgr1.setRole(NID_LOCAL, VDID2, NONE);
+ mastershipMgr1.relinquishMastership(VDID2);
+ assertNull("wrong role:", mastershipMgr1.getMasterFor(VDID2));
+
+ //provide NID_OTHER as backup and relinquish
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr1.getMasterFor(VDID1));
+ mastershipMgr1.setRole(NID_OTHER, VDID1, STANDBY);
+ mastershipMgr1.relinquishMastership(VDID1);
+ assertEquals("wrong master:", NID_OTHER, mastershipMgr1.getMasterFor(VDID1));
+ }
+
+ @Test
+ public void requestRoleFor() {
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ mastershipMgr1.setRole(NID_OTHER, VDID2, MASTER);
+
+ //local should be master for one but standby for other
+ assertEquals("wrong role:", MASTER, Futures.getUnchecked(mastershipMgr1.requestRoleFor(VDID1)));
+ assertEquals("wrong role:", STANDBY, Futures.getUnchecked(mastershipMgr1.requestRoleFor(VDID2)));
+ }
+
+ @Test
+ public void getMasterFor() {
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ mastershipMgr1.setRole(NID_OTHER, VDID2, MASTER);
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr1.getMasterFor(VDID1));
+ assertEquals("wrong master:", NID_OTHER, mastershipMgr1.getMasterFor(VDID2));
+
+ //have NID_OTHER hand over VDID2 to NID_LOCAL
+ mastershipMgr1.setRole(NID_LOCAL, VDID2, MASTER);
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr1.getMasterFor(VDID2));
+ }
+
+ @Test
+ public void getDevicesOf() {
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ mastershipMgr1.setRole(NID_LOCAL, VDID2, STANDBY);
+ assertEquals("should be one device:", 1, mastershipMgr1.getDevicesOf(NID_LOCAL).size());
+ //hand both devices to NID_LOCAL
+ mastershipMgr1.setRole(NID_LOCAL, VDID2, MASTER);
+ assertEquals("should be two devices:", 2, mastershipMgr1.getDevicesOf(NID_LOCAL).size());
+ }
+
+ @Test
+ public void termService() {
+ MastershipTermService ts = mastershipMgr1;
+
+ //term = 1 for both
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ assertEquals("inconsistent term: ", 1,
+ ts.getMastershipTerm(VDID1).termNumber());
+
+ //hand devices to NID_LOCAL and back: term = 1 + 2
+ mastershipMgr1.setRole(NID_OTHER, VDID1, MASTER);
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ assertEquals("inconsistent terms: ",
+ 3, ts.getMastershipTerm(VDID1).termNumber());
+ }
+
+ @Test
+ public void balanceWithVnets() {
+
+ testClusterService.put(CNODE1, ControllerNode.State.ACTIVE);
+ testClusterService.put(CNODE2, ControllerNode.State.ACTIVE);
+
+ mastershipMgr1.setRole(NID_LOCAL, VDID1, MASTER);
+ mastershipMgr1.setRole(NID_LOCAL, VDID2, MASTER);
+ assertEquals("wrong local role:", MASTER, mastershipMgr1.getLocalRole(VDID1));
+ assertEquals("wrong local role:", MASTER, mastershipMgr1.getLocalRole(VDID2));
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr1.getMasterFor(VDID1));
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr1.getMasterFor(VDID2));
+
+ //do balancing according to vnet Id.
+ mastershipMgr1.balanceRoles();
+ assertEquals("wrong master:", NID1, mastershipMgr1.getMasterFor(VDID1));
+ assertEquals("wrong master:", NID1, mastershipMgr1.getMasterFor(VDID2));
+
+ mastershipMgr2.setRole(NID_LOCAL, VDID3, MASTER);
+ mastershipMgr2.setRole(NID_LOCAL, VDID4, MASTER);
+ assertEquals("wrong local role:", MASTER, mastershipMgr2.getLocalRole(VDID3));
+ assertEquals("wrong local role:", MASTER, mastershipMgr2.getLocalRole(VDID4));
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr2.getMasterFor(VDID3));
+ assertEquals("wrong master:", NID_LOCAL, mastershipMgr2.getMasterFor(VDID4));
+
+ //do balancing according to vnet Id.
+ mastershipMgr2.balanceRoles();
+ assertEquals("wrong master:", NID2, mastershipMgr2.getMasterFor(VDID3));
+ assertEquals("wrong master:", NID2, mastershipMgr2.getMasterFor(VDID4));
+
+ // make N1 inactive
+ testClusterService.put(CNODE1, ControllerNode.State.INACTIVE);
+ mastershipMgr1.balanceRoles();
+ assertEquals("wrong master:", NID2, mastershipMgr1.getMasterFor(VDID1));
+ assertEquals("wrong master:", NID2, mastershipMgr1.getMasterFor(VDID2));
+ }
+
+ private final class TestClusterService extends ClusterServiceAdapter {
+
+ final Map<NodeId, ControllerNode> nodes = new HashMap<>();
+ final Map<NodeId, ControllerNode.State> nodeStates = new HashMap<>();
+
+ ControllerNode local = new DefaultControllerNode(NID_LOCAL, LOCALHOST);
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return local;
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ return Sets.newHashSet(nodes.values());
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ return nodes.get(nodeId);
+ }
+
+ @Override
+ public ControllerNode.State getState(NodeId nodeId) {
+ return nodeStates.get(nodeId);
+ }
+
+ public void put(ControllerNode cn, ControllerNode.State state) {
+ nodes.put(cn.id(), cn);
+ nodeStates.put(cn.id(), state);
+ }
+ }
+
+ private final class TestSimpleMastershipStore extends SimpleVirtualMastershipStore
+ implements VirtualNetworkMastershipStore {
+
+ public TestSimpleMastershipStore(ClusterService clusterService) {
+ super.clusterService = clusterService;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManagerTest.java
new file mode 100644
index 0000000..3eb6e88
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManagerTest.java
@@ -0,0 +1,332 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Maps;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onosproject.TestApplicationId;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkMeterStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.event.VirtualListenerRegistryManager;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualMeterProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualMeterStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.DefaultMeterFeatures;
+import org.onosproject.net.meter.DefaultMeterRequest;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterFeaturesKey;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterState;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+/**
+ * Virtual Network meter manager tests.
+ */
+public class VirtualNetworkMeterManagerTest extends VirtualNetworkTestUtil {
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final NodeId NID_LOCAL = new NodeId("local");
+ private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private TestableIntentService intentService = new FakeIntentManager();
+ private ServiceDirectory testDirectory;
+ private VirtualProviderManager providerRegistryService;
+
+ private EventDeliveryService eventDeliveryService;
+ VirtualListenerRegistryManager listenerRegistryManager =
+ VirtualListenerRegistryManager.getInstance();
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ private SimpleVirtualMeterStore meterStore;
+
+ private VirtualNetworkMeterManager meterManager1;
+ private VirtualNetworkMeterManager meterManager2;
+
+ private TestProvider provider = new TestProvider();
+ private VirtualMeterProviderService providerService1;
+ private VirtualMeterProviderService providerService2;
+
+ private ApplicationId appId;
+
+ private Meter m1;
+ private Meter m2;
+ private MeterRequest.Builder m1Request;
+ private MeterRequest.Builder m2Request;
+
+ private Map<MeterId, Meter> meters = Maps.newHashMap();
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+ CoreService coreService = new TestCoreService();
+ TestStorageService storageService = new TestStorageService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", storageService);
+ virtualNetworkManagerStore.activate();
+
+ meterStore = new SimpleVirtualMeterStore();
+
+ providerRegistryService = new VirtualProviderManager();
+ providerRegistryService.registerProvider(provider);
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ TestUtils.setField(manager, "coreService", coreService);
+
+ eventDeliveryService = new TestEventDispatcher();
+ NetTestTools.injectEventDispatcher(manager, eventDeliveryService);
+// eventDeliveryService.addSink(VirtualEvent.class, listenerRegistryManager);
+
+ appId = new TestApplicationId("MeterManagerTest");
+
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(VirtualProviderRegistryService.class, providerRegistryService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(StorageService.class, storageService)
+ .add(VirtualNetworkMeterStore.class, meterStore);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ vnet1 = setupVirtualNetworkTopology(manager, TID1);
+ vnet2 = setupVirtualNetworkTopology(manager, TID2);
+
+ meterManager1 = new VirtualNetworkMeterManager(manager, vnet1.id());
+ meterManager2 = new VirtualNetworkMeterManager(manager, vnet2.id());
+
+ providerService1 = (VirtualMeterProviderService)
+ providerRegistryService.getProviderService(vnet1.id(), VirtualMeterProvider.class);
+ providerService2 = (VirtualMeterProviderService)
+ providerRegistryService.getProviderService(vnet2.id(), VirtualMeterProvider.class);
+
+ assertTrue("provider should be registered",
+ providerRegistryService.getProviders().contains(provider.id()));
+
+ setupMeterTestVariables();
+ }
+
+ @After
+ public void tearDown() {
+ providerRegistryService.unregisterProvider(provider);
+ assertFalse("provider should not be registered",
+ providerRegistryService.getProviders().contains(provider.id()));
+
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+
+ virtualNetworkManagerStore.deactivate();
+ }
+
+ /** Test for meter submit(). */
+ @Test
+ public void testAddition() {
+ meterManager1.submit(m1Request.add());
+
+ assertTrue("The meter was not added",
+ meterManager1.getAllMeters().size() == 1);
+ assertThat(meterManager1.getMeter(VDID1, MeterId.meterId(1)), is(m1));
+
+ assertTrue("The meter shouldn't be added for vnet2",
+ meterManager2.getAllMeters().size() == 0);
+ }
+
+ /** Test for meter remove(). */
+ @Test
+ public void testRemove() {
+ meterManager1.submit(m1Request.add());
+ meterManager1.withdraw(m1Request.remove(), m1.id());
+
+ assertThat(meterManager1.getMeter(VDID1, MeterId.meterId(1)).state(),
+ is(MeterState.PENDING_REMOVE));
+
+ providerService1.pushMeterMetrics(m1.deviceId(), Collections.emptyList());
+
+ assertTrue("The meter was not removed", meterManager1.getAllMeters().size() == 0);
+ assertTrue("The meter shouldn't be added for vnet2",
+ meterManager2.getAllMeters().size() == 0);
+ }
+
+ /** Test for meter submit with multiple devices. */
+ @Test
+ public void testMultipleDevice() {
+ meterManager1.submit(m1Request.add());
+ meterManager1.submit(m2Request.add());
+
+ assertTrue("The meters were not added",
+ meterManager1.getAllMeters().size() == 2);
+ assertTrue("The meter shouldn't be added for vnet2",
+ meterManager2.getAllMeters().size() == 0);
+
+ assertThat(meterManager1.getMeter(VDID1, MeterId.meterId(1)), is(m1));
+ assertThat(meterManager1.getMeter(VDID2, MeterId.meterId(1)), is(m2));
+ }
+
+ /** Test for meter features inside store. */
+ @Test
+ public void testMeterFeatures() {
+ //Test for virtual network 1
+ assertEquals(meterStore.getMaxMeters(vnet1.id(),
+ MeterFeaturesKey.key(VDID1)), 255L);
+ assertEquals(meterStore.getMaxMeters(vnet1.id(),
+ MeterFeaturesKey.key(VDID2)), 2);
+ //Test for virtual network 2
+ assertEquals(meterStore.getMaxMeters(vnet2.id(),
+ MeterFeaturesKey.key(VDID1)), 100);
+ assertEquals(meterStore.getMaxMeters(vnet2.id(),
+ MeterFeaturesKey.key(VDID2)), 10);
+ }
+
+ /** Set variables such as meters and request required for testing. */
+ private void setupMeterTestVariables() {
+ Band band = DefaultBand.builder()
+ .ofType(Band.Type.DROP)
+ .withRate(500)
+ .build();
+
+ m1 = DefaultMeter.builder()
+ .forDevice(VDID1)
+ .fromApp(appId)
+ .withId(MeterId.meterId(1))
+ .withUnit(Meter.Unit.KB_PER_SEC)
+ .withBands(Collections.singletonList(band))
+ .build();
+
+ m2 = DefaultMeter.builder()
+ .forDevice(VDID2)
+ .fromApp(appId)
+ .withId(MeterId.meterId(1))
+ .withUnit(Meter.Unit.KB_PER_SEC)
+ .withBands(Collections.singletonList(band))
+ .build();
+
+ m1Request = DefaultMeterRequest.builder()
+ .forDevice(VDID1)
+ .fromApp(appId)
+ .withUnit(Meter.Unit.KB_PER_SEC)
+ .withBands(Collections.singletonList(band));
+
+ m2Request = DefaultMeterRequest.builder()
+ .forDevice(VDID2)
+ .fromApp(appId)
+ .withUnit(Meter.Unit.KB_PER_SEC)
+ .withBands(Collections.singletonList(band));
+
+ meterStore.storeMeterFeatures(vnet1.id(),
+ DefaultMeterFeatures.builder().forDevice(VDID1)
+ .withMaxMeters(255L)
+ .withBandTypes(new HashSet<>())
+ .withUnits(new HashSet<>())
+ .hasStats(false)
+ .hasBurst(false)
+ .withMaxBands((byte) 0)
+ .withMaxColors((byte) 0)
+ .build());
+ meterStore.storeMeterFeatures(vnet1.id(),
+ DefaultMeterFeatures.builder().forDevice(VDID2)
+ .withMaxMeters(2)
+ .withBandTypes(new HashSet<>())
+ .withUnits(new HashSet<>())
+ .hasBurst(false)
+ .hasStats(false)
+ .withMaxBands((byte) 0)
+ .withMaxColors((byte) 0)
+ .build());
+
+ meterStore.storeMeterFeatures(vnet2.id(),
+ DefaultMeterFeatures.builder().forDevice(VDID1)
+ .withMaxMeters(100L)
+ .withBandTypes(new HashSet<>())
+ .withUnits(new HashSet<>())
+ .hasStats(false)
+ .hasBurst(false)
+ .withMaxBands((byte) 0)
+ .withMaxColors((byte) 0)
+ .build());
+ meterStore.storeMeterFeatures(vnet2.id(),
+ DefaultMeterFeatures.builder().forDevice(VDID2)
+ .withMaxMeters(10)
+ .withBandTypes(new HashSet<>())
+ .withUnits(new HashSet<>())
+ .hasBurst(false)
+ .hasStats(false)
+ .withMaxBands((byte) 0)
+ .withMaxColors((byte) 0)
+ .build());
+ }
+
+ private class TestProvider
+ extends AbstractVirtualProvider
+ implements VirtualMeterProvider {
+
+ protected TestProvider() {
+ super(PID);
+ }
+
+ @Override
+ public void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperations meterOps) {
+
+ }
+
+ @Override
+ public void performMeterOperation(NetworkId networkId, DeviceId deviceId,
+ MeterOperation meterOp) {
+ meters.put(meterOp.meter().id(), meterOp.meter());
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerTest.java
new file mode 100644
index 0000000..66ccb73
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerTest.java
@@ -0,0 +1,416 @@
+/*
+ * 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.impl;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.TestApplicationId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.impl.provider.VirtualProviderManager;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowObjectiveStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualFlowRuleStore;
+import org.onosproject.incubator.net.virtual.store.impl.SimpleVirtualPacketStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
+import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.onosproject.net.packet.PacketPriority.REACTIVE;
+
+/**
+ * Junit tests for VirtualNetworkPacketManager using SimpleVirtualPacketStore.
+ */
+public class VirtualNetworkPacketManagerTest extends VirtualNetworkTestUtil {
+
+ private static final int PROCESSOR_PRIORITY = 1;
+
+ protected VirtualNetworkManager manager;
+ protected DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService = new TestCoreService();
+ protected TestServiceDirectory testDirectory;
+ private EventDeliveryService eventDeliveryService;
+ private VirtualProviderManager providerRegistryService;
+
+ private VirtualNetwork vnet1;
+ private VirtualNetwork vnet2;
+
+ private VirtualPacketProvider provider = new TestPacketProvider();
+ protected VirtualNetworkPacketStore packetStore = new SimpleVirtualPacketStore();
+
+ protected VirtualNetworkPacketManager packetManager1;
+ private VirtualNetworkPacketManager packetManager2;
+
+ private ApplicationId appId = new TestApplicationId("VirtualPacketManagerTest");
+
+ private VirtualFlowRuleProvider flowRuleProvider = new TestFlowRuleProvider();
+ private SimpleVirtualFlowRuleStore flowRuleStore;
+ private SimpleVirtualFlowObjectiveStore flowObjectiveStore;
+ protected StorageService storageService = new TestStorageService();
+
+ @Before
+ public void setUp() throws TestUtils.TestUtilsException {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", storageService);
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ flowObjectiveStore = new SimpleVirtualFlowObjectiveStore();
+ TestUtils.setField(flowObjectiveStore, "storageService", storageService);
+ flowObjectiveStore.activate();
+ flowRuleStore = new SimpleVirtualFlowRuleStore();
+ flowRuleStore.activate();
+
+ providerRegistryService = new VirtualProviderManager();
+ providerRegistryService.registerProvider(provider);
+ providerRegistryService.registerProvider(flowRuleProvider);
+
+ testDirectory = new TestServiceDirectory()
+ .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
+ .add(CoreService.class, coreService)
+ .add(VirtualProviderRegistryService.class, providerRegistryService)
+ .add(EventDeliveryService.class, eventDeliveryService)
+ .add(ClusterService.class, new ClusterServiceAdapter())
+ .add(VirtualNetworkFlowRuleStore.class, flowRuleStore)
+ .add(VirtualNetworkFlowObjectiveStore.class, flowObjectiveStore)
+ .add(VirtualNetworkPacketStore.class, packetStore);
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ eventDeliveryService = new TestEventDispatcher();
+ NetTestTools.injectEventDispatcher(manager, eventDeliveryService);
+
+ manager.activate();
+
+ vnet1 = VirtualNetworkTestUtil.setupVirtualNetworkTopology(manager, TID1);
+ vnet2 = VirtualNetworkTestUtil.setupVirtualNetworkTopology(manager, TID2);
+
+ packetManager1 = new VirtualNetworkPacketManager(manager, vnet1.id());
+ packetManager2 = new VirtualNetworkPacketManager(manager, vnet2.id());
+ }
+
+ /**
+ * Tests the correct usage of addProcessor() for a outbound packet.
+ */
+ @Test
+ public void addProcessorTest() {
+ PacketProcessor testProcessor = new TestProcessor();
+ packetManager1.addProcessor(testProcessor, PROCESSOR_PRIORITY);
+
+ assertEquals("1 processor expected", 1,
+ packetManager1.getProcessors().size());
+ assertEquals("0 processor expected", 0,
+ packetManager2.getProcessors().size());
+
+ assertEquals("not equal packet processor", testProcessor,
+ packetManager1.getProcessors().get(0).processor());
+ assertEquals("not equal packet processor priority", PROCESSOR_PRIORITY,
+ packetManager1.getProcessors().get(0).priority());
+ }
+
+ /**
+ * Tests the correct usage of addProcessor() for a outbound packet.
+ */
+ @Test
+ public void removeProcessorTest() {
+ PacketProcessor testProcessor = new TestProcessor();
+ packetManager1.addProcessor(testProcessor, PROCESSOR_PRIORITY);
+
+ assertEquals("1 processor expected", 1,
+ packetManager1.getProcessors().size());
+ assertEquals("0 processor expected", 0,
+ packetManager2.getProcessors().size());
+
+ packetManager1.removeProcessor(testProcessor);
+
+ assertEquals("0 processor expected", 0,
+ packetManager1.getProcessors().size());
+ assertEquals("0 processor expected", 0,
+ packetManager2.getProcessors().size());
+ }
+
+ /**
+ * Tests the correct usage of emit() for a outbound packet.
+ */
+ @Test
+ public void emitTest() {
+ OutboundPacket packet =
+ new DefaultOutboundPacket(VDID1, DefaultTrafficTreatment.emptyTreatment(), ByteBuffer.allocate(5));
+ packetManager1.emit(packet);
+ assertEquals("Packet not emitted correctly", packet, emittedPacket);
+ }
+
+ /**
+ * Tests the addition and removal of packet requests for a device.
+ *
+ * @throws TestUtils.TestUtilsException
+ */
+ @Test
+ public void requestAndCancelPacketsForDeviceTest() throws TestUtils.TestUtilsException {
+ TestFlowObjectiveService testFlowObjectiveService = new TestFlowObjectiveService();
+ TestUtils.setField(packetManager1, "objectiveService", testFlowObjectiveService);
+ TrafficSelector ts = DefaultTrafficSelector.emptySelector();
+ Optional<DeviceId> optionalDeviceId = Optional.of(VDID3);
+
+ // add first request
+ packetManager1.requestPackets(ts, CONTROL, appId, optionalDeviceId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, CONTROL, ADD);
+
+ // add same request as first
+ packetManager1.requestPackets(ts, CONTROL, appId, optionalDeviceId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, CONTROL, ADD);
+
+ // add second request
+ packetManager1.requestPackets(ts, REACTIVE, appId, optionalDeviceId);
+ assertEquals("2 packets expected", 2, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, REACTIVE, ADD);
+
+ // cancel second request
+ packetManager1.cancelPackets(ts, REACTIVE, appId, optionalDeviceId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, REACTIVE, REMOVE);
+
+ // cancel second request again
+ packetManager1.cancelPackets(ts, REACTIVE, appId, optionalDeviceId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, REACTIVE, REMOVE);
+
+ // cancel first request
+ packetManager1.cancelPackets(ts, CONTROL, appId, optionalDeviceId);
+ assertEquals("0 packet expected", 0, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectiveForDevice(VDID3, ts, CONTROL, REMOVE);
+ }
+
+ /**
+ * Tests the addition and removal of packet requests for all devices in a virtual
+ * network.
+ *
+ * @throws TestUtils.TestUtilsException
+ */
+ @Test
+ public void requestAndCancelPacketsForVnetTest() throws TestUtils.TestUtilsException {
+ TestFlowObjectiveService testFlowObjectiveService = new TestFlowObjectiveService();
+ TestUtils.setField(packetManager1, "objectiveService", testFlowObjectiveService);
+ TrafficSelector ts = DefaultTrafficSelector.emptySelector();
+ Set<VirtualDevice> vnet1Devices = manager.getVirtualDevices(vnet1.id());
+
+ // add first request
+ packetManager1.requestPackets(ts, CONTROL, appId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, CONTROL, ADD);
+
+ // add same request as first
+ packetManager1.requestPackets(ts, CONTROL, appId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, CONTROL, ADD);
+
+ // add second request
+ packetManager1.requestPackets(ts, REACTIVE, appId);
+ assertEquals("2 packets expected", 2, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, REACTIVE, ADD);
+
+ // cancel second request
+ packetManager1.cancelPackets(ts, REACTIVE, appId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, REACTIVE, REMOVE);
+
+ // cancel second request again
+ packetManager1.cancelPackets(ts, REACTIVE, appId);
+ assertEquals("1 packet expected", 1, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, REACTIVE, REMOVE);
+
+ // cancel first request
+ packetManager1.cancelPackets(ts, CONTROL, appId);
+ assertEquals("0 packet expected", 0, packetManager1.getRequests().size());
+ testFlowObjectiveService.validateObjectives(vnet1Devices, ts, CONTROL, REMOVE);
+ }
+
+ protected OutboundPacket emittedPacket = null;
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+
+ @Override
+ public ApplicationId registerApplication(String name) {
+ return appId;
+ }
+ }
+
+ private class TestPacketProvider extends AbstractVirtualProvider
+ implements VirtualPacketProvider {
+
+ /**
+ * Creates a provider with the supplied identifier.
+ */
+ protected TestPacketProvider() {
+ super(new ProviderId("test-packet",
+ "org.onosproject.virtual.test-packet"));
+ }
+
+ @Override
+ public void emit(NetworkId networkId, OutboundPacket packet) {
+ emittedPacket = packet;
+ }
+
+ }
+
+ private class TestProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+
+ }
+ }
+
+ private class TestFlowObjectiveService extends FlowObjectiveServiceAdapter {
+ // track objectives received for each device
+ private final Map<DeviceId, Set<ForwardingObjective>> deviceFwdObjs = new HashMap<>();
+
+ @Override
+ public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ deviceFwdObjs.compute(deviceId, (deviceId1, forwardingObjectives) -> {
+ if (forwardingObjectives == null) {
+ return Sets.newHashSet(forwardingObjective);
+ }
+ forwardingObjectives.add(forwardingObjective);
+ return forwardingObjectives;
+ }
+ );
+ }
+
+ private void validateObjectives(Set<VirtualDevice> vdevs, TrafficSelector ts,
+ PacketPriority pp, Objective.Operation op) {
+ assertNotNull("set of devices must not be null", vdevs);
+ for (VirtualDevice vdev: vdevs) {
+ assertTrue("Forwarding objective must exist for device " + vdev.id(),
+ deviceHasObjective(vdev.id(), ts, pp, op));
+ }
+ }
+
+ private void validateObjectiveForDevice(DeviceId deviceId, TrafficSelector ts,
+ PacketPriority pp, Objective.Operation op) {
+ assertNotNull("deviceId must not be null", deviceId);
+ assertTrue("Forwarding objective must exist for device " + deviceId,
+ deviceHasObjective(deviceId, ts, pp, op));
+ }
+
+ private boolean deviceHasObjective(DeviceId deviceId, TrafficSelector ts,
+ PacketPriority pp, Objective.Operation op) {
+ Set<ForwardingObjective> fos = deviceFwdObjs.get(deviceId);
+ if (fos != null) {
+ for (ForwardingObjective fo: fos) {
+ if (fo.selector().equals(ts)
+ && fo.priority() == pp.priorityValue()
+ && fo.op().equals(op)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private class TestFlowRuleProvider extends AbstractVirtualProvider
+ implements VirtualFlowRuleProvider {
+
+ protected TestFlowRuleProvider() {
+ super(new ProviderId("test", "org.onosproject.virtual.testprovider"));
+ }
+
+ @Override
+ public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
+
+ }
+
+ @Override
+ public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
+
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerWithDistStoreTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerWithDistStoreTest.java
new file mode 100644
index 0000000..914d709
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPacketManagerWithDistStoreTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ComponentContextAdapter;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualPacketStore;
+import org.onosproject.mastership.MastershipServiceAdapter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationServiceAdapter;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+import static org.junit.Assert.assertNull;
+
+/**
+ * Junit tests for VirtualNetworkPacketManager using DistributedVirtualPacketStore..
+ * This test class extends VirtualNetworkPacketManagerTest - all the tests defined in
+ * VirtualNetworkPacketManagerTest will run using DistributedVirtualPacketStore.
+ */
+public class VirtualNetworkPacketManagerWithDistStoreTest extends VirtualNetworkPacketManagerTest {
+
+ private DistributedVirtualPacketStore distStore;
+ private ClusterService clusterService = new ClusterServiceAdapter();
+
+ @Before
+ public void setUp() throws TestUtils.TestUtilsException {
+ setUpDistPacketStore();
+ super.setUp();
+ TestUtils.setField(packetManager1, "storageService", storageService);
+ }
+
+ private void setUpDistPacketStore() throws TestUtils.TestUtilsException {
+ distStore = new DistributedVirtualPacketStore();
+ TestUtils.setField(distStore, "cfgService", new ComponentConfigAdapter());
+ TestUtils.setField(distStore, "storageService", storageService);
+ TestUtils.setField(distStore, "clusterService", clusterService);
+ TestUtils.setField(distStore, "communicationService", new TestClusterCommunicationService());
+ TestUtils.setField(distStore, "mastershipService", new TestMastershipService());
+
+ distStore.activate(new ComponentContextAdapter());
+ packetStore = distStore; // super.setUp() will cause Distributed store to be used.
+ }
+
+ @After
+ public void tearDown() {
+ distStore.deactivate();
+ }
+
+ @Override
+ @Test
+ @Ignore("Ignore until there is MastershipService support for virtual devices")
+ public void emitTest() {
+ super.emitTest();
+ }
+
+ /**
+ * Tests the correct usage of emit() for a outbound packet - master of packet's
+ * sendThrough is not local node.
+ */
+ @Test
+ @Ignore("Ignore until there is MastershipService support for virtual devices")
+ public void emit2Test() {
+ OutboundPacket packet =
+ new DefaultOutboundPacket(VDID2, DefaultTrafficTreatment.emptyTreatment(), ByteBuffer.allocate(5));
+ packetManager1.emit(packet);
+ assertNull("Packet should not have been emmitted", emittedPacket);
+ }
+
+ private final class TestMastershipService extends MastershipServiceAdapter {
+ @Override
+ public NodeId getMasterFor(DeviceId deviceId) {
+ if (VDID1.equals(deviceId)) {
+ return clusterService.getLocalNode().id();
+ }
+ return new NodeId("abc");
+ }
+ }
+
+ private final class TestClusterCommunicationService extends ClusterCommunicationServiceAdapter {
+ @Override
+ public <M> CompletableFuture<Void> unicast(M message, MessageSubject subject,
+ Function<M, byte[]> encoder, NodeId toNodeId) {
+ return new CompletableFuture<>();
+ }
+ }
+
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManagerTest.java
new file mode 100644
index 0000000..b8db7ea
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkPathManagerTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.LinkWeigherAdapter;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Junit tests for VirtualNetworkPathService.
+ */
+public class VirtualNetworkPathManagerTest extends TestDeviceParams {
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+
+ private TestServiceDirectory testDirectory;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ CoreService coreService = new TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Sets up an empty virtual network (no devices, links).
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupEmptyVnet() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ return manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ }
+
+ /**
+ * Creates a virtual network for further testing.
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupVnet() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ VirtualDevice virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID3);
+ VirtualDevice virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID4);
+
+ ConnectPoint cp11 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice1, 1);
+ ConnectPoint cp12 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice1, 2);
+ ConnectPoint cp23 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice2, 3);
+ ConnectPoint cp24 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice2, 4);
+ ConnectPoint cp35 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice3, 5);
+ ConnectPoint cp36 = createConnectPointAndVirtualPort(virtualNetwork, virtualDevice3, 6);
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp11, cp23);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp23, cp11);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp24, cp35);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp35, cp24);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp12, cp36);
+ virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp36, cp12);
+ virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+ return virtualNetwork;
+ }
+
+ /**
+ * Creates a connect point and related virtual port.
+ *
+ * @param vnet virtual network
+ * @param vDev virtual device
+ * @param portNumber port number
+ * @return connect point
+ */
+ private ConnectPoint createConnectPointAndVirtualPort(
+ VirtualNetwork vnet, VirtualDevice vDev, long portNumber) {
+ ConnectPoint cp = new ConnectPoint(vDev.id(), PortNumber.portNumber(portNumber));
+ manager.createVirtualPort(vnet.id(), cp.deviceId(), cp.port(),
+ new ConnectPoint(vDev.id(), cp.port()));
+ return cp;
+ }
+
+ /**
+ * Tests getPaths(), getDisjointPaths()
+ * on a non-empty virtual network.
+ */
+ @Test
+ public void testGetPathsOnNonEmptyVnet() {
+ VirtualNetwork vnet = setupVnet();
+ PathService pathService = manager.get(vnet.id(), PathService.class);
+
+ // src and dest are in vnet and are connected by a virtual link
+ Set<Path> paths = pathService.getPaths(DID1, DID3);
+ validatePaths(paths, 1, 1, DID1, DID3, 1.0);
+
+ LinkWeigher linkWeight = new LinkWeigherAdapter(2.0);
+ paths = pathService.getPaths(DID1, DID3, linkWeight);
+ validatePaths(paths, 1, 1, DID1, DID3, 2.0);
+
+ Set<DisjointPath> disjointPaths = pathService.getDisjointPaths(DID1, DID3);
+ validatePaths(disjointPaths, 1, 1, DID1, DID3, 1.0);
+
+ disjointPaths = pathService.getDisjointPaths(DID1, DID3, linkWeight);
+ validatePaths(disjointPaths, 1, 1, DID1, DID3, 2.0);
+
+ // src and dest are in vnet but are not connected
+ paths = pathService.getPaths(DID4, DID3);
+ assertEquals("incorrect path count", 0, paths.size());
+
+ disjointPaths = pathService.getDisjointPaths(DID4, DID3);
+ assertEquals("incorrect path count", 0, disjointPaths.size());
+
+ // src is in vnet, but dest is not in vnet.
+ DeviceId nonExistentDeviceId = DeviceId.deviceId("nonExistentDevice");
+ paths = pathService.getPaths(DID2, nonExistentDeviceId);
+ assertEquals("incorrect path count", 0, paths.size());
+
+ disjointPaths = pathService.getDisjointPaths(DID2, nonExistentDeviceId);
+ assertEquals("incorrect path count", 0, disjointPaths.size());
+ }
+
+ /**
+ * Tests getPaths(), getDisjointPaths()
+ * on an empty virtual network.
+ */
+ @Test
+ public void testGetPathsOnEmptyVnet() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ PathService pathService = manager.get(vnet.id(), PathService.class);
+
+ Set<Path> paths = pathService.getPaths(DID1, DID3);
+ assertEquals("incorrect path count", 0, paths.size());
+
+ Set<DisjointPath> disjointPaths = pathService.getDisjointPaths(DID1, DID3);
+ assertEquals("incorrect path count", 0, disjointPaths.size());
+ }
+
+ /**
+ * Tests getPaths() using a null source device on an empty virtual network.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPathsWithNullSrc() {
+ VirtualNetwork vnet = setupEmptyVnet();
+ PathService pathService = manager.get(vnet.id(), PathService.class);
+ pathService.getPaths(null, DID3);
+ }
+
+ /**
+ * Tests getPaths() using a null destination device on a non-empty virtual network.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPathsWithNullDest() {
+ VirtualNetwork vnet = setupVnet();
+ PathService pathService = manager.get(vnet.id(), PathService.class);
+ pathService.getPaths(DID1, null);
+ }
+
+
+ // Makes sure the set of paths meets basic expectations.
+ private void validatePaths(Set<? extends Path> paths, int count, int length,
+ ElementId src, ElementId dst, double cost) {
+ assertEquals("incorrect path count", count, paths.size());
+ for (Path path : paths) {
+ assertEquals("incorrect length", length, path.links().size());
+ assertEquals("incorrect source", src, path.src().elementId());
+ assertEquals("incorrect destination", dst, path.dst().elementId());
+ assertEquals("incorrect cost", ScalarWeight.toWeight(cost), path.weight());
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTestUtil.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTestUtil.java
new file mode 100644
index 0000000..d1b7274
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTestUtil.java
@@ -0,0 +1,107 @@
+/*
+ * 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.impl;
+
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+
+import static org.onosproject.net.DeviceId.deviceId;
+
+public class VirtualNetworkTestUtil extends TestDeviceParams {
+
+ protected static final TenantId TID1 = TenantId.tenantId("tid1");
+ protected static final TenantId TID2 = TenantId.tenantId("tid2");
+
+ protected static final DeviceId VDID1 = deviceId("of:foo_v");
+ protected static final DeviceId VDID2 = deviceId("of:bar_v");
+ protected static final DeviceId VDID3 = deviceId("of:who_v");
+ protected static final DeviceId VDID4 = deviceId("of:what_v");
+
+ protected static final DeviceId PHYDID1 = deviceId("physical:1");
+ protected static final DeviceId PHYDID2 = deviceId("physical:2");
+ protected static final DeviceId PHYDID3 = deviceId("physical:3");
+ protected static final DeviceId PHYDID4 = deviceId("physical:4");
+
+ /**
+ * Method to create the virtual network for further testing.
+ *
+ * @return virtual network
+ */
+ public static VirtualNetwork setupVirtualNetworkTopology(VirtualNetworkManager manager,
+ TenantId tenantId) {
+ manager.registerTenantId(tenantId);
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(tenantId);
+
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), VDID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), VDID2);
+ VirtualDevice virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), VDID3);
+ VirtualDevice virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), VDID4);
+
+ ConnectPoint vcp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+ ConnectPoint cp1 = new ConnectPoint(DID1, PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), vcp1.deviceId(), vcp1.port(), cp1);
+
+ ConnectPoint vcp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+ ConnectPoint cp2 = new ConnectPoint(DID1, PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), vcp2.deviceId(), vcp2.port(), cp2);
+
+ ConnectPoint vcp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+ ConnectPoint cp3 = new ConnectPoint(DID2, PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), vcp3.deviceId(), vcp3.port(), cp3);
+
+ ConnectPoint vcp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+ ConnectPoint cp4 = new ConnectPoint(DID2, PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), vcp4.deviceId(), vcp4.port(), cp4);
+
+ ConnectPoint vcp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+ ConnectPoint cp5 = new ConnectPoint(DID3, PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), vcp5.deviceId(), vcp5.port(), cp5);
+
+ ConnectPoint vcp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+ ConnectPoint cp6 = new ConnectPoint(DID3, PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), vcp6.deviceId(), vcp6.port(), cp6);
+
+ DistributedVirtualNetworkStore virtualNetworkManagerStore =
+ (DistributedVirtualNetworkStore) manager.store;
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), vcp1, vcp3);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), vcp3, vcp1);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), vcp4, vcp5);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), vcp5, vcp4);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), vcp2, vcp6);
+ virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), vcp6, vcp2);
+ virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+ return virtualNetwork;
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java
new file mode 100644
index 0000000..8fb7904
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java
@@ -0,0 +1,642 @@
+/*
+ * 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.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.LinkWeigherAdapter;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyCluster;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Junit tests for VirtualNetworkTopologyService.
+ */
+public class VirtualNetworkTopologyManagerTest extends TestDeviceParams {
+
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService;
+ private TestServiceDirectory testDirectory;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+ coreService = new VirtualNetworkTopologyManagerTest.TestCoreService();
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ manager.store = virtualNetworkManagerStore;
+ manager.coreService = coreService;
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+ }
+
+ @After
+ public void tearDown() {
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Method to create the virtual network for further testing.
+ *
+ * @return virtual network
+ */
+ private VirtualNetwork setupVirtualNetworkTopology() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ VirtualDevice virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ VirtualDevice virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID3);
+ VirtualDevice virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID4);
+
+ ConnectPoint cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), cp1.deviceId(), cp1.port(), cp1);
+
+ ConnectPoint cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), cp2.deviceId(), cp2.port(), cp2);
+
+ ConnectPoint cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+ manager.createVirtualPort(virtualNetwork.id(), cp3.deviceId(), cp3.port(), cp3);
+
+ ConnectPoint cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+ manager.createVirtualPort(virtualNetwork.id(), cp4.deviceId(), cp4.port(), cp4);
+
+ ConnectPoint cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+ manager.createVirtualPort(virtualNetwork.id(), cp5.deviceId(), cp5.port(), cp5);
+
+ ConnectPoint cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+ manager.createVirtualPort(virtualNetwork.id(), cp6.deviceId(), cp6.port(), cp6);
+
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp2, cp6);
+ virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp6, cp2);
+ virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+ return virtualNetwork;
+ }
+
+ /**
+ * Tests the currentTopology() method.
+ */
+ @Test
+ public void testCurrentTopology() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+ assertNotNull("The topology should not be null.", topology);
+ }
+
+ /**
+ * Test isLatest() method using a null topology.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsLatestByNullTopology() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+
+ // test the isLatest() method with a null topology.
+ topologyService.isLatest(null);
+ }
+
+ /**
+ * Test isLatest() method.
+ */
+ @Test
+ public void testIsLatest() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ // test the isLatest() method.
+ assertTrue("This should be latest topology", topologyService.isLatest(topology));
+
+ VirtualDevice srcVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the isLatest() method where a new device has been added to the current topology.
+ assertFalse("This should not be latest topology", topologyService.isLatest(topology));
+
+ topology = topologyService.currentTopology();
+ ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), src.deviceId(), src.port(),
+ new ConnectPoint(srcVirtualDevice.id(), src.port()));
+
+ ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), dst.deviceId(), dst.port(),
+ new ConnectPoint(dstVirtualDevice.id(), dst.port()));
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), src, dst);
+
+ // test the isLatest() method where a new link has been added to the current topology.
+ assertFalse("This should not be latest topology", topologyService.isLatest(topology));
+ }
+
+ /**
+ * Test getGraph() method.
+ */
+ @Test
+ public void testGetGraph() {
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ // test the getGraph() method.
+ assertNotNull("The graph should not be null.", topologyService.getGraph(topology));
+ }
+
+ /**
+ * Test getClusters() method.
+ */
+ @Test
+ public void testGetClusters() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+
+ Topology topology = topologyService.currentTopology();
+
+ // test the getClusters() method.
+ assertNotNull("The clusters should not be null.", topologyService.getClusters(topology));
+ assertEquals("The clusters size did not match.", 2, topologyService.getClusters(topology).size());
+ }
+
+ /**
+ * Test getCluster() method using a null cluster identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetClusterUsingNullClusterId() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ Set<TopologyCluster> clusters = topologyService.getClusters(topology);
+ TopologyCluster cluster = clusters.stream().findFirst().get();
+
+ // test the getCluster() method with a null cluster identifier
+ TopologyCluster cluster1 = topologyService.getCluster(topology, null);
+ }
+
+ /**
+ * Test getCluster() method.
+ */
+ @Test
+ public void testGetCluster() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ Set<TopologyCluster> clusters = topologyService.getClusters(topology);
+ assertNotNull("The clusters should not be null.", clusters);
+ assertEquals("The clusters size did not match.", 2, clusters.size());
+
+ // test the getCluster() method.
+ TopologyCluster cluster = clusters.stream().findFirst().get();
+ assertNotNull("The cluster should not be null.", cluster);
+ TopologyCluster cluster1 = topologyService.getCluster(topology, cluster.id());
+ assertNotNull("The cluster should not be null.", cluster1);
+ assertEquals("The cluster ID did not match.", cluster.id(), cluster1.id());
+ }
+
+ /**
+ * Test getClusterDevices() methods with a null cluster.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetClusterDevicesUsingNullCluster() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+ Set<TopologyCluster> clusters = topologyService.getClusters(topology);
+
+ // test the getClusterDevices() method using a null cluster.
+ Object[] objects = clusters.stream().toArray();
+ assertNotNull("The cluster should not be null.", objects);
+ Set<DeviceId> clusterDevices = topologyService.getClusterDevices(topology, null);
+ }
+
+ /**
+ * Test getClusterLinks() methods with a null cluster.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetClusterLinksUsingNullCluster() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+ Set<TopologyCluster> clusters = topologyService.getClusters(topology);
+
+ // test the getClusterLinks() method using a null cluster.
+ Object[] objects = clusters.stream().toArray();
+ assertNotNull("The cluster should not be null.", objects);
+ Set<Link> clusterLinks = topologyService.getClusterLinks(topology, null);
+ }
+
+ /**
+ * Test getClusterDevices() and getClusterLinks() methods.
+ */
+ @Test
+ public void testGetClusterDevicesLinks() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ Set<TopologyCluster> clusters = topologyService.getClusters(topology);
+ assertNotNull("The clusters should not be null.", clusters);
+ assertEquals("The clusters size did not match.", 2, clusters.size());
+
+ // test the getClusterDevices() method.
+ Object[] objects = clusters.stream().toArray();
+ assertNotNull("The cluster should not be null.", objects);
+ Set<DeviceId> clusterDevices = topologyService.getClusterDevices(topology, (TopologyCluster) objects[0]);
+ assertNotNull("The devices should not be null.", clusterDevices);
+ assertEquals("The devices size did not match.", 3, clusterDevices.size());
+ Set<DeviceId> clusterDevices1 = topologyService.getClusterDevices(topology, (TopologyCluster) objects[1]);
+ assertNotNull("The devices should not be null.", clusterDevices1);
+ assertEquals("The devices size did not match.", 1, clusterDevices1.size());
+
+ // test the getClusterLinks() method.
+ Set<Link> clusterLinks = topologyService.getClusterLinks(topology, (TopologyCluster) objects[0]);
+ assertNotNull("The links should not be null.", clusterLinks);
+ assertEquals("The links size did not match.", 6, clusterLinks.size());
+ Set<Link> clusterLinks1 = topologyService.getClusterLinks(topology, (TopologyCluster) objects[1]);
+ assertNotNull("The links should not be null.", clusterLinks1);
+ assertEquals("The links size did not match.", 0, clusterLinks1.size());
+ }
+
+ /**
+ * Test getPaths() method using a null src device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPathsUsingNullSrcDeviceId() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getPaths() method using a null src device identifier.
+ Set<Path> paths = topologyService.getPaths(topology, null, dstVirtualDevice.id());
+ }
+
+ /**
+ * Test getPaths() method using a null dst device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPathsUsingNullDstDeviceId() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getPaths() method using a null dst device identifier.
+ Set<Path> paths = topologyService.getPaths(topology, srcVirtualDevice.id(), null);
+ }
+
+ /**
+ * Test getPaths() method using a null weight.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetPathsUsingNullWeight() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getPaths() method using a null weight.
+ Set<Path> paths = topologyService.getPaths(topology, srcVirtualDevice.id(),
+ dstVirtualDevice.id(), (LinkWeigher) null);
+ }
+
+ /**
+ * Test getPaths() and getPaths() by weight methods.
+ */
+ @Test
+ public void testGetPaths() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getPaths() method.
+ Set<Path> paths = topologyService.getPaths(topology, srcVirtualDevice.id(), dstVirtualDevice.id());
+ assertNotNull("The paths should not be null.", paths);
+ assertEquals("The paths size did not match.", 1, paths.size());
+
+ // test the getPaths() by weight method.
+ LinkWeigher weight = new LinkWeigherAdapter(1.0);
+ Set<Path> paths1 = topologyService.getPaths(topology, srcVirtualDevice.id(), dstVirtualDevice.id(), weight);
+ assertNotNull("The paths should not be null.", paths1);
+ assertEquals("The paths size did not match.", 1, paths1.size());
+ Path path = paths1.iterator().next();
+ assertEquals("wrong path length", 1, path.links().size());
+ assertEquals("wrong path cost", ScalarWeight.toWeight(1.0), path.weight());
+ }
+
+ /**
+ * Test getDisjointPaths() methods using a null src device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDisjointPathsUsingNullSrcDeviceId() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getDisjointPaths() method using a null src device identifier.
+ Set<DisjointPath> paths = topologyService.getDisjointPaths(topology, null, dstVirtualDevice.id());
+ }
+
+ /**
+ * Test getDisjointPaths() methods using a null dst device identifier.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDisjointPathsUsingNullDstDeviceId() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getDisjointPaths() method using a null dst device identifier.
+ Set<DisjointPath> paths = topologyService.getDisjointPaths(topology, srcVirtualDevice.id(), null);
+ }
+
+ /**
+ * Test getDisjointPaths() methods using a null weight.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDisjointPathsUsingNullWeight() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getDisjointPaths() method using a null weight.
+ Set<DisjointPath> paths = topologyService.getDisjointPaths(topology, srcVirtualDevice.id(),
+ dstVirtualDevice.id(), (LinkWeigher) null);
+ }
+
+ /**
+ * Test getDisjointPaths() methods using a null risk profile.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testGetDisjointPathsUsingNullRiskProfile() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getDisjointPaths() method using a null risk profile.
+ Set<DisjointPath> paths = topologyService.getDisjointPaths(topology, srcVirtualDevice.id(),
+ dstVirtualDevice.id(), (Map<Link, Object>) null);
+ }
+
+ /**
+ * Test getDisjointPaths() methods.
+ */
+ @Test
+ public void testGetDisjointPaths() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID2);
+
+ // test the getDisjointPaths() method.
+ Set<DisjointPath> paths = topologyService.getDisjointPaths(topology, srcVirtualDevice.id(),
+ dstVirtualDevice.id());
+ assertNotNull("The paths should not be null.", paths);
+ assertEquals("The paths size did not match.", 1, paths.size());
+
+ // test the getDisjointPaths() method using a weight.
+ LinkWeigher weight = new LinkWeigherAdapter(1.0);
+ Set<DisjointPath> paths1 = topologyService.getDisjointPaths(topology, srcVirtualDevice.id(),
+ dstVirtualDevice.id(), weight);
+ assertNotNull("The paths should not be null.", paths1);
+ assertEquals("The paths size did not match.", 1, paths1.size());
+ }
+
+ /**
+ * Test isInfrastructure() method using a null connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsInfrastructureUsingNullConnectPoint() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ // test the isInfrastructure() method using a null connect point.
+ Boolean isInfrastructure = topologyService.isInfrastructure(topology, null);
+ }
+
+ /**
+ * Test isInfrastructure() method.
+ */
+ @Test
+ public void testIsInfrastructure() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ VirtualDevice dstVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID4);
+ ConnectPoint cp1 = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+ ConnectPoint cp2 = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+
+ // test the isInfrastructure() method.
+ Boolean isInfrastructure = topologyService.isInfrastructure(topology, cp1);
+ assertTrue("The connect point should be infrastructure.", isInfrastructure);
+
+ isInfrastructure = topologyService.isInfrastructure(topology, cp2);
+ assertFalse("The connect point should not be infrastructure.", isInfrastructure);
+ }
+
+ /**
+ * Test isBroadcastPoint() method using a null connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsBroadcastUsingNullConnectPoint() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ // test the isInfrastructure() method using a null connect point.
+ Boolean isInfrastructure = topologyService.isBroadcastPoint(topology, null);
+ }
+
+ /**
+ * Test isBroadcastPoint() method.
+ */
+ @Test
+ public void testIsBroadcastPoint() {
+ VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
+
+ TopologyService topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ Topology topology = topologyService.currentTopology();
+
+ VirtualDevice srcVirtualDevice = getVirtualDevice(virtualNetwork.id(), DID1);
+ ConnectPoint cp = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+
+ // test the isBroadcastPoint() method.
+ Boolean isBroadcastPoint = topologyService.isBroadcastPoint(topology, cp);
+ assertTrue("The connect point should be a broadcast point.", isBroadcastPoint);
+ }
+
+ /**
+ * Return the virtual device matching the device identifier.
+ *
+ * @param networkId virtual network identifier
+ * @param deviceId device identifier
+ * @return virtual device
+ */
+ private VirtualDevice getVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+ Optional<VirtualDevice> foundDevice = manager.getVirtualDevices(networkId)
+ .stream()
+ .filter(device -> deviceId.equals(device.id()))
+ .findFirst();
+ if (foundDevice.isPresent()) {
+ return foundDevice.get();
+ }
+ return null;
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ ApplicationId appId;
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+
+ @Override
+ public ApplicationId registerApplication(String name) {
+ appId = new DefaultApplicationId(1, name);
+ return appId;
+ }
+
+ @Override
+ public ApplicationId getAppId(String name) {
+ return appId;
+ }
+ }
+
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
new file mode 100644
index 0000000..8e11e6f
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
@@ -0,0 +1,386 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.graph.Weight;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminServiceAdapter;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyServiceAdapter;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+
+public class DefaultVirtualFlowRuleProviderTest {
+ private static final ProviderId PID = new ProviderId("of", "foo");
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:001");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:002");
+ private static final PortNumber PORT_NUM1 = PortNumber.portNumber(1);
+ private static final PortNumber PORT_NUM2 = PortNumber.portNumber(2);
+
+ private static final DefaultAnnotations ANNOTATIONS =
+ DefaultAnnotations.builder().set("foo", "bar").build();
+
+ private static final Device DEV1 =
+ new DefaultDevice(PID, DID1, Device.Type.SWITCH, "", "", "", "", null);
+ private static final Device DEV2 =
+ new DefaultDevice(PID, DID2, Device.Type.SWITCH, "", "", "", "", null);
+ private static final Port PORT11 =
+ new DefaultPort(DEV1, PORT_NUM1, true, ANNOTATIONS);
+ private static final Port PORT12 =
+ new DefaultPort(DEV1, PORT_NUM2, true, ANNOTATIONS);
+ private static final Port PORT21 =
+ new DefaultPort(DEV2, PORT_NUM1, true, ANNOTATIONS);
+ private static final Port PORT22 =
+ new DefaultPort(DEV2, PORT_NUM2, true, ANNOTATIONS);
+
+ private static final ConnectPoint CP11 = new ConnectPoint(DID1, PORT_NUM1);
+ private static final ConnectPoint CP12 = new ConnectPoint(DID1, PORT_NUM2);
+ private static final ConnectPoint CP21 = new ConnectPoint(DID2, PORT_NUM1);
+ private static final ConnectPoint CP22 = new ConnectPoint(DID2, PORT_NUM2);
+ private static final Link LINK1 = DefaultLink.builder()
+ .src(CP12).dst(CP21).providerId(PID).type(Link.Type.DIRECT).build();
+
+ private static final NetworkId VNET_ID = NetworkId.networkId(1);
+ private static final DeviceId VDID = DeviceId.deviceId("of:100");
+
+ private static final VirtualNetwork VNET = new DefaultVirtualNetwork(
+ VNET_ID, TenantId.tenantId("t1"));
+ private static final VirtualDevice VDEV =
+ new DefaultVirtualDevice(VNET_ID, VDID);
+ private static final VirtualPort VPORT1 =
+ new DefaultVirtualPort(VNET_ID, VDEV, PORT_NUM1, CP11);
+ private static final VirtualPort VPORT2 =
+ new DefaultVirtualPort(VNET_ID, VDEV, PORT_NUM2, CP22);
+
+ private static final int TIMEOUT = 10;
+
+ protected DefaultVirtualFlowRuleProvider virtualProvider;
+
+ private ApplicationId vAppId;
+
+ @Before
+ public void setUp() {
+ virtualProvider = new DefaultVirtualFlowRuleProvider();
+
+ virtualProvider.deviceService = new TestDeviceService();
+ virtualProvider.coreService = new TestCoreService();
+ virtualProvider.vnService =
+ new TestVirtualNetworkAdminService();
+ virtualProvider.topologyService = new TestTopologyService();
+ virtualProvider.flowRuleService = new TestFlowRuleService();
+ virtualProvider.providerRegistryService = new VirtualProviderManager();
+
+ virtualProvider.activate();
+ vAppId = new TestApplicationId(0, "Virtual App");
+ }
+
+ @After
+ public void tearDown() {
+ virtualProvider.deactivate();
+ virtualProvider.deviceService = null;
+ virtualProvider.coreService = null;
+ }
+
+ @Test
+ public void devirtualizeFlowRuleWithInPort() {
+ TrafficSelector ts = DefaultTrafficSelector.builder()
+ .matchInPort(PORT_NUM1).build();
+ TrafficTreatment tr = DefaultTrafficTreatment.builder()
+ .setOutput(PORT_NUM2).build();
+
+ FlowRule r1 = DefaultFlowRule.builder()
+ .forDevice(VDID)
+ .withSelector(ts)
+ .withTreatment(tr)
+ .withPriority(10)
+ .fromApp(vAppId)
+ .makeTemporary(TIMEOUT)
+ .build();
+
+ virtualProvider.applyFlowRule(VNET_ID, r1);
+
+ assertEquals("2 rules should exist", 2,
+ virtualProvider.flowRuleService.getFlowRuleCount());
+
+ Set<FlowEntry> phyRules = new HashSet<>();
+ for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID1)) {
+ phyRules.add(i);
+ }
+ for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID2)) {
+ phyRules.add(i);
+ }
+
+ FlowRule in = null;
+ FlowRule out = null;
+
+ for (FlowRule rule : phyRules) {
+
+ L2ModificationInstruction i = (L2ModificationInstruction)
+ rule.treatment().allInstructions().get(0);
+
+ if (i.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+ in = rule;
+ } else {
+ out = rule;
+ }
+
+ }
+
+ assertEquals(DID1, in.deviceId());
+ assertEquals(DID2, out.deviceId());
+ }
+
+ @Test
+ public void devirtualizeFlowRuleWithoutInPort() {
+ TrafficSelector ts = DefaultTrafficSelector.builder().build();
+ TrafficTreatment tr = DefaultTrafficTreatment.builder()
+ .setOutput(PORT_NUM2).build();
+
+ FlowRule r1 = DefaultFlowRule.builder()
+ .forDevice(VDID)
+ .withSelector(ts)
+ .withTreatment(tr)
+ .withPriority(10)
+ .fromApp(vAppId)
+ .makeTemporary(TIMEOUT)
+ .build();
+
+ virtualProvider.applyFlowRule(VNET_ID, r1);
+
+ assertEquals("3 rules should exist", 3,
+ virtualProvider.flowRuleService.getFlowRuleCount());
+
+ FlowRule inFromDID1 = null;
+ FlowRule inFromDID2 = null;
+ FlowRule out = null;
+
+ Set<FlowEntry> phyRules = new HashSet<>();
+ for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID1)) {
+ phyRules.add(i);
+ }
+ for (FlowEntry i : virtualProvider.flowRuleService.getFlowEntries(DID2)) {
+ phyRules.add(i);
+ }
+
+ for (FlowRule rule : phyRules) {
+ for (Instruction inst : rule.treatment().allInstructions()) {
+ if (inst.type() == Instruction.Type.L2MODIFICATION) {
+ L2ModificationInstruction i = (L2ModificationInstruction) inst;
+ if (i.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+ inFromDID1 = rule;
+ break;
+ } else {
+ out = rule;
+ break;
+ }
+ } else {
+ inFromDID2 = rule;
+ break;
+ }
+ }
+ }
+
+ assertEquals(DID1, inFromDID1.deviceId());
+ assertEquals(DID2, inFromDID2.deviceId());
+ assertEquals(DID2, out.deviceId());
+ }
+
+ @Test
+ public void removeVirtualizeFlowRule() {
+ TrafficSelector ts = DefaultTrafficSelector.builder().build();
+ TrafficTreatment tr = DefaultTrafficTreatment.builder()
+ .setOutput(PORT_NUM2).build();
+
+ FlowRule r1 = DefaultFlowRule.builder()
+ .forDevice(VDID)
+ .withSelector(ts)
+ .withTreatment(tr)
+ .withPriority(10)
+ .fromApp(vAppId)
+ .makeTemporary(TIMEOUT)
+ .build();
+
+ virtualProvider.removeFlowRule(VNET_ID, r1);
+
+ assertEquals("0 rules should exist", 0,
+ virtualProvider.flowRuleService.getFlowRuleCount());
+ }
+
+
+ private static class TestDeviceService extends DeviceServiceAdapter {
+ @Override
+ public int getDeviceCount() {
+ return 2;
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return ImmutableList.of(DEV1, DEV2);
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices() {
+ return getDevices();
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return deviceId.equals(DID2) ? DEV2 : DEV1;
+ }
+ }
+
+ private static class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public ApplicationId registerApplication(String name) {
+ return new TestApplicationId(1, name);
+ }
+ }
+
+ private static class TestApplicationId extends DefaultApplicationId {
+ public TestApplicationId(int id, String name) {
+ super(id, name);
+ }
+ }
+
+ private class TestVirtualNetworkAdminService
+ extends VirtualNetworkAdminServiceAdapter {
+
+ @Override
+ public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+ return ImmutableSet.of(VDEV);
+ }
+
+ @Override
+ public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
+ return new HashSet<>();
+ }
+
+ @Override
+ public Set<VirtualPort> getVirtualPorts(NetworkId networkId,
+ DeviceId deviceId) {
+ return ImmutableSet.of(VPORT1, VPORT2);
+ }
+
+ @Override
+ public ApplicationId getVirtualNetworkApplicationId(NetworkId networkId) {
+ return vAppId;
+ }
+ }
+
+ private static class TestTopologyService extends TopologyServiceAdapter {
+
+ Weight oneHundred = ScalarWeight.toWeight(100);
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
+ DefaultPath path = new DefaultPath(PID, ImmutableList.of(LINK1),
+ oneHundred, ANNOTATIONS);
+ return ImmutableSet.of(path);
+ }
+
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+ LinkWeigher weigher) {
+ DefaultPath path = new DefaultPath(PID, ImmutableList.of(LINK1),
+ oneHundred, ANNOTATIONS);
+ return ImmutableSet.of(path);
+ }
+
+ }
+
+ private static class TestFlowRuleService extends FlowRuleServiceAdapter {
+ static Set<FlowRule> ruleCollection = new HashSet<>();
+
+ @Override
+ public int getFlowRuleCount() {
+ return ruleCollection.size();
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ return ruleCollection.stream()
+ .filter(r -> r.deviceId().equals(deviceId))
+ .map(DefaultFlowEntry::new)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public void applyFlowRules(FlowRule... flowRules) {
+ ruleCollection.addAll(Arrays.asList(flowRules));
+ }
+
+ @Override
+ public void removeFlowRules(FlowRule... flowRules) {
+ Set<FlowRule> candidates = new HashSet<>();
+ for (FlowRule rule : flowRules) {
+ ruleCollection.stream()
+ .filter(r -> r.exactMatch(rule))
+ .forEach(candidates::add);
+ }
+ ruleCollection.removeAll(candidates);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java
new file mode 100644
index 0000000..a17a0db
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualPacketProviderTest.java
@@ -0,0 +1,331 @@
+/*
+ * 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.impl.provider;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminServiceAdapter;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+public class DefaultVirtualPacketProviderTest {
+ private static final String SRC_MAC_ADDR = "00:00:00:00:00:00";
+ private static final String DST_MAC_ADDR = "00:00:00:00:00:01";
+ private static final ProviderId PID = new ProviderId("of", "foo");
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:001");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:002");
+ private static final PortNumber PORT_NUM1 = PortNumber.portNumber(1);
+ private static final PortNumber PORT_NUM2 = PortNumber.portNumber(2);
+ private static final PortNumber PORT_NUM3 = PortNumber.portNumber(3);
+ private static final PortNumber PORT_NUM4 = PortNumber.portNumber(4);
+
+ private static final DefaultAnnotations ANNOTATIONS =
+ DefaultAnnotations.builder().set("foo", "bar").build();
+
+ private static final Device DEV1 =
+ new DefaultDevice(PID, DID1, Device.Type.SWITCH, "", "", "", "", null);
+ private static final Device DEV2 =
+ new DefaultDevice(PID, DID2, Device.Type.SWITCH, "", "", "", "", null);
+ private static final Port PORT11 =
+ new DefaultPort(DEV1, PORT_NUM1, true, ANNOTATIONS);
+ private static final Port PORT12 =
+ new DefaultPort(DEV1, PORT_NUM2, true, ANNOTATIONS);
+ private static final Port PORT21 =
+ new DefaultPort(DEV2, PORT_NUM3, true, ANNOTATIONS);
+ private static final Port PORT22 =
+ new DefaultPort(DEV2, PORT_NUM4, true, ANNOTATIONS);
+
+ private static final ConnectPoint CP11 = new ConnectPoint(DID1, PORT_NUM1);
+ private static final ConnectPoint CP12 = new ConnectPoint(DID1, PORT_NUM2);
+ private static final ConnectPoint CP21 = new ConnectPoint(DID2, PORT_NUM3);
+ private static final ConnectPoint CP22 = new ConnectPoint(DID2, PORT_NUM4);
+ private static final Link LINK1 = DefaultLink.builder()
+ .src(CP12).dst(CP21).providerId(PID).type(Link.Type.DIRECT).build();
+
+ private static final TenantId TENANT_ID = TenantId.tenantId("1");
+ private static final NetworkId VNET_ID = NetworkId.networkId(1);
+ private static final DeviceId VDID = DeviceId.deviceId("of:100");
+
+ private static final PortNumber VPORT_NUM1 = PortNumber.portNumber(10);
+ private static final PortNumber VPORT_NUM2 = PortNumber.portNumber(11);
+
+ private static final VirtualNetwork VNET = new DefaultVirtualNetwork(
+ VNET_ID, TenantId.tenantId("t1"));
+ private static final VirtualDevice VDEV =
+ new DefaultVirtualDevice(VNET_ID, VDID);
+ private static final VirtualPort VPORT1 =
+ new DefaultVirtualPort(VNET_ID, VDEV, VPORT_NUM1, CP11);
+ private static final VirtualPort VPORT2 =
+ new DefaultVirtualPort(VNET_ID, VDEV, VPORT_NUM2, CP22);
+ private static final ConnectPoint VCP11 = new ConnectPoint(VDID, VPORT_NUM1);
+ private static final ConnectPoint VCP12 = new ConnectPoint(VDID, VPORT_NUM2);
+
+ protected DefaultVirtualPacketProvider virtualProvider;
+ protected TestPacketService testPacketService;
+ protected TestVirtualPacketProviderService providerService;
+
+ private VirtualProviderManager providerManager;
+
+ private ApplicationId vAppId;
+
+ @Before
+ public void setUp() {
+ virtualProvider = new DefaultVirtualPacketProvider();
+
+ virtualProvider.coreService = new CoreServiceAdapter();
+ virtualProvider.vnaService =
+ new TestVirtualNetworkAdminService();
+
+ providerService = new TestVirtualPacketProviderService();
+
+ testPacketService = new TestPacketService();
+ virtualProvider.packetService = testPacketService;
+
+ providerManager = new VirtualProviderManager();
+ virtualProvider.providerRegistryService = providerManager;
+ providerManager.registerProviderService(VNET_ID, providerService);
+
+ virtualProvider.activate();
+ vAppId = new TestApplicationId(0, "Virtual App");
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+
+ virtualProvider.startPacketHandling();
+ }
+
+ @After
+ public void tearDown() {
+ virtualProvider.deactivate();
+ virtualProvider.coreService = null;
+ virtualProvider.vnaService = null;
+ }
+
+
+ /** Test the virtual outbound packet is delivered to a proper (physical)
+ * device.
+ */
+ @Test
+ public void devirtualizePacket() {
+ TrafficTreatment tr = DefaultTrafficTreatment.builder()
+ .setOutput(VPORT_NUM1).build();
+ ByteBuffer data = ByteBuffer.wrap("abc".getBytes());
+
+ OutboundPacket vOutPacket = new DefaultOutboundPacket(VDID, tr, data);
+
+ virtualProvider.emit(VNET_ID, vOutPacket);
+
+ assertEquals("The count should be 1", 1,
+ testPacketService.getRequestedPacketCount());
+
+ OutboundPacket pOutPacket = testPacketService.getRequestedPacket(0);
+
+ assertEquals("The packet should be requested on DEV1", DID1,
+ pOutPacket.sendThrough());
+
+ PortNumber outPort = pOutPacket.treatment()
+ .allInstructions()
+ .stream()
+ .filter(i -> i.type() == Instruction.Type.OUTPUT)
+ .map(i -> (Instructions.OutputInstruction) i)
+ .map(i -> i.port())
+ .findFirst().get();
+ assertEquals("The packet should be out at PORT1 of DEV1", PORT_NUM1,
+ outPort);
+ }
+
+ /** Test the physical packet context is delivered to a proper (physical)
+ * virtual network and device.
+ */
+ @Test
+ public void virtualizePacket() {
+ Ethernet eth = new Ethernet();
+ eth.setSourceMACAddress(SRC_MAC_ADDR);
+ eth.setDestinationMACAddress(DST_MAC_ADDR);
+ eth.setVlanID((short) 1);
+ eth.setPayload(null);
+
+ InboundPacket pInPacket =
+ new DefaultInboundPacket(CP22, eth,
+ ByteBuffer.wrap(eth.serialize()));
+
+ PacketContext pContext =
+ new TestPacketContext(System.nanoTime(), pInPacket, null, false);
+
+ testPacketService.sendTestPacketContext(pContext);
+
+ PacketContext vContext = providerService.getRequestedPacketContext(0);
+ InboundPacket vInPacket = vContext.inPacket();
+
+ assertEquals("the packet should be received from VCP12",
+ VCP12, vInPacket.receivedFrom());
+
+ assertEquals("VLAN tag should be excludede", VlanId.UNTAGGED,
+ vInPacket.parsed().getVlanID());
+ }
+
+ private class TestPacketContext extends DefaultPacketContext {
+
+ /**
+ * Creates a new packet context.
+ *
+ * @param time creation time
+ * @param inPkt inbound packet
+ * @param outPkt outbound packet
+ * @param block whether the context is blocked or not
+ */
+ protected TestPacketContext(long time, InboundPacket inPkt,
+ OutboundPacket outPkt, boolean block) {
+ super(time, inPkt, outPkt, block);
+ }
+
+ @Override
+ public void send() {
+
+ }
+ }
+
+ private static class TestApplicationId extends DefaultApplicationId {
+ public TestApplicationId(int id, String name) {
+ super(id, name);
+ }
+ }
+
+ private static class TestVirtualNetworkAdminService
+ extends VirtualNetworkAdminServiceAdapter {
+
+ @Override
+ public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
+ return ImmutableSet.of(VNET);
+ }
+
+ @Override
+ public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+ return ImmutableSet.of(VDEV);
+ }
+
+ @Override
+ public Set<VirtualPort> getVirtualPorts(NetworkId networkId,
+ DeviceId deviceId) {
+ return ImmutableSet.of(VPORT1, VPORT2);
+ }
+
+ @Override
+ public Set<TenantId> getTenantIds() {
+ return ImmutableSet.of(TENANT_ID);
+ }
+
+ }
+
+ private static class TestVirtualPacketProviderService
+ extends AbstractVirtualProviderService<VirtualPacketProvider>
+ implements VirtualPacketProviderService {
+
+ static List<PacketContext> requestedContext = new LinkedList();
+ static List<NetworkId> requestedNetworkId = new LinkedList();
+
+ @Override
+ public VirtualPacketProvider provider() {
+ return null;
+ }
+
+ PacketContext getRequestedPacketContext(int index) {
+ return requestedContext.get(index);
+ }
+
+ @Override
+ public void processPacket(PacketContext context) {
+ requestedContext.add(context);
+ }
+ }
+
+ private static class TestPacketService extends PacketServiceAdapter {
+ static List<OutboundPacket> requestedPacket = new LinkedList();
+ static PacketProcessor processor = null;
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ this.processor = processor;
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ requestedPacket.add(packet);
+ }
+
+ OutboundPacket getRequestedPacket(int index) {
+ return requestedPacket.get(index);
+ }
+
+ int getRequestedPacketCount() {
+ return requestedPacket.size();
+ }
+
+ void sendTestPacketContext(PacketContext context) {
+ processor.process(context);
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java
new file mode 100644
index 0000000..ee03783
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualNetworkTopologyProviderTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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.impl.provider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
+import org.onosproject.incubator.net.virtual.impl.VirtualNetworkManager;
+import org.onosproject.incubator.net.virtual.store.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.TestDeviceParams;
+import org.onosproject.net.intent.FakeIntentManager;
+import org.onosproject.net.intent.TestableIntentService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkTopologyProvider.
+ */
+public class VirtualNetworkTopologyProviderTest extends TestDeviceParams {
+
+ private final String tenantIdValue1 = "TENANT_ID1";
+
+ private VirtualNetwork virtualNetwork;
+ private VirtualDevice virtualDevice1;
+ private VirtualDevice virtualDevice2;
+ private VirtualDevice virtualDevice3;
+ private VirtualDevice virtualDevice4;
+ private VirtualDevice virtualDevice5;
+ private ConnectPoint cp1;
+ private ConnectPoint cp2;
+ private ConnectPoint cp3;
+ private ConnectPoint cp4;
+ private ConnectPoint cp5;
+ private ConnectPoint cp6;
+ private ConnectPoint cp7;
+ private ConnectPoint cp8;
+ private ConnectPoint cp9;
+
+ private VirtualNetworkManager manager;
+ private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+ private CoreService coreService;
+ private DefaultVirtualNetworkProvider topologyProvider;
+ private TopologyService topologyService;
+ private TestableIntentService intentService = new FakeIntentManager();
+ private TestServiceDirectory testDirectory;
+ private final VirtualNetworkRegistryAdapter virtualNetworkRegistry = new VirtualNetworkRegistryAdapter();
+
+ private static final int MAX_WAIT_TIME = 5;
+ private static final int MAX_PERMITS = 1;
+ private static Semaphore changed;
+
+ private Set<Set<ConnectPoint>> clusters;
+
+ @Before
+ public void setUp() throws Exception {
+
+ virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+ coreService = new VirtualNetworkTopologyProviderTest.TestCoreService();
+
+ TestUtils.setField(virtualNetworkManagerStore, "coreService", coreService);
+ TestUtils.setField(virtualNetworkManagerStore, "storageService",
+ new TestStorageService());
+ virtualNetworkManagerStore.activate();
+
+ manager = new VirtualNetworkManager();
+ TestUtils.setField(manager, "coreService", coreService);
+ TestUtils.setField(manager, "store", virtualNetworkManagerStore);
+ TestUtils.setField(manager, "intentService", intentService);
+ NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+
+ testDirectory = new TestServiceDirectory();
+ TestUtils.setField(manager, "serviceDirectory", testDirectory);
+
+ manager.activate();
+
+ manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+ virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+
+ topologyService = manager.get(virtualNetwork.id(), TopologyService.class);
+ topologyProvider = new DefaultVirtualNetworkProvider();
+ topologyProvider.topologyService = topologyService;
+ topologyProvider.providerRegistry = virtualNetworkRegistry;
+ topologyProvider.activate();
+
+ setupVirtualNetworkTopology();
+ changed = new Semaphore(0, true);
+ }
+
+ @After
+ public void tearDown() {
+ topologyProvider.deactivate();
+ virtualNetworkManagerStore.deactivate();
+ manager.deactivate();
+ NetTestTools.injectEventDispatcher(manager, null);
+ }
+
+ /**
+ * Method to create the virtual network for further testing.
+ **/
+ private void setupVirtualNetworkTopology() {
+ virtualDevice1 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID1);
+ virtualDevice2 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID2);
+ virtualDevice3 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID3);
+ virtualDevice4 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID4);
+ virtualDevice5 =
+ manager.createVirtualDevice(virtualNetwork.id(), DID5);
+
+ cp1 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+ PortNumber.portNumber(1), cp1);
+
+ cp2 = new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(2));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(),
+ PortNumber.portNumber(2), cp2);
+
+ cp3 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(3));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+ PortNumber.portNumber(3), cp3);
+
+ cp4 = new ConnectPoint(virtualDevice2.id(), PortNumber.portNumber(4));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(),
+ PortNumber.portNumber(4), cp4);
+
+ cp5 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(5));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+ PortNumber.portNumber(5), cp5);
+
+ cp6 = new ConnectPoint(virtualDevice3.id(), PortNumber.portNumber(6));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(),
+ PortNumber.portNumber(6), cp6);
+
+ cp7 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(7));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+ PortNumber.portNumber(7), cp7);
+
+ cp8 = new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(8));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice4.id(),
+ PortNumber.portNumber(8), cp8);
+
+ cp9 = new ConnectPoint(virtualDevice5.id(), PortNumber.portNumber(9));
+ manager.createVirtualPort(virtualNetwork.id(), virtualDevice5.id(),
+ PortNumber.portNumber(9), cp9);
+
+ VirtualLink link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
+ virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
+ virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
+ virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link5 = manager.createVirtualLink(virtualNetwork.id(), cp8, cp9);
+ virtualNetworkManagerStore.updateLink(link5, link5.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link6 = manager.createVirtualLink(virtualNetwork.id(), cp9, cp8);
+ virtualNetworkManagerStore.updateLink(link6, link6.tunnelId(), Link.State.ACTIVE);
+
+ clusters = null;
+ }
+
+ /**
+ * Test isTraversable() method using a null source connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsTraversableNullSrc() {
+ // test the isTraversable() method with a null source connect point.
+ topologyProvider.isTraversable(null, cp3);
+ }
+
+ /**
+ * Test isTraversable() method using a null destination connect point.
+ */
+ @Test(expected = NullPointerException.class)
+ public void testIsTraversableNullDst() {
+ // test the isTraversable() method with a null destination connect point.
+ topologyProvider.isTraversable(cp1, null);
+ }
+
+ /**
+ * Test isTraversable() method.
+ */
+ @Test
+ public void testIsTraversable() {
+ // test the isTraversable() method.
+ assertTrue("These two connect points should be traversable.",
+ topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+ new ConnectPoint(cp3.elementId(), cp3.port())));
+ assertTrue("These two connect points should be traversable.",
+ topologyProvider.isTraversable(new ConnectPoint(cp1.elementId(), cp1.port()),
+ new ConnectPoint(cp5.elementId(), cp5.port())));
+ assertFalse("These two connect points should not be traversable.",
+ topologyProvider.isTraversable(
+ new ConnectPoint(virtualDevice1.id(), PortNumber.portNumber(1)),
+ new ConnectPoint(virtualDevice4.id(), PortNumber.portNumber(6))));
+ }
+
+ /**
+ * Test the topologyChanged() method.
+ */
+ @Test
+ public void testTopologyChanged() {
+ // Initial setup is two clusters of devices/links.
+ assertEquals("The cluster count did not match.", 2,
+ topologyService.currentTopology().clusterCount());
+
+ // Adding this link will join the two clusters together.
+ List<Event> reasons = new ArrayList<>();
+ VirtualLink link = manager.createVirtualLink(virtualNetwork.id(), cp6, cp7);
+ virtualNetworkManagerStore.updateLink(link, link.tunnelId(), Link.State.ACTIVE);
+ VirtualLink link2 = manager.createVirtualLink(virtualNetwork.id(), cp7, cp6);
+ virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
+
+ reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link));
+ reasons.add(new LinkEvent(LinkEvent.Type.LINK_ADDED, link2));
+ TopologyEvent event = new TopologyEvent(
+ TopologyEvent.Type.TOPOLOGY_CHANGED,
+ topologyService.currentTopology(),
+ reasons);
+
+ topologyProvider.topologyListener.event(event);
+
+ // Wait for the topology changed event, and that the topologyChanged method was called.
+ try {
+ if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for topology changed event.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception." + e.getMessage());
+ }
+
+ // Validate that the topology changed method received a single cluster of connect points.
+ // This means that the two previous clusters have now joined into a single cluster.
+ assertEquals("The cluster count did not match.", 1, this.clusters.size());
+ assertEquals("The cluster count did not match.", 1,
+ topologyService.currentTopology().clusterCount());
+
+ // Now remove the virtual link to split it back into two clusters.
+ manager.removeVirtualLink(virtualNetwork.id(), link.src(), link.dst());
+ manager.removeVirtualLink(virtualNetwork.id(), link2.src(), link2.dst());
+ assertEquals("The cluster count did not match.", 2,
+ topologyService.currentTopology().clusterCount());
+
+ reasons = new ArrayList<>();
+ reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link));
+ reasons.add(new LinkEvent(LinkEvent.Type.LINK_REMOVED, link2));
+ event = new TopologyEvent(
+ TopologyEvent.Type.TOPOLOGY_CHANGED,
+ topologyService.currentTopology(),
+ reasons);
+
+ topologyProvider.topologyListener.event(event);
+
+ // Wait for the topology changed event, and that the topologyChanged method was called.
+ try {
+ if (!changed.tryAcquire(MAX_PERMITS, MAX_WAIT_TIME, TimeUnit.SECONDS)) {
+ fail("Failed to wait for topology changed event.");
+ }
+ } catch (InterruptedException e) {
+ fail("Semaphore exception." + e.getMessage());
+ }
+
+ // Validate that the topology changed method received two clusters of connect points.
+ // This means that the single previous clusters has now split into two clusters.
+ assertEquals("The cluster count did not match.", 2, this.clusters.size());
+ }
+
+ /**
+ * Virtual network registry implementation for this test class.
+ */
+ private class VirtualNetworkRegistryAdapter implements VirtualNetworkProviderRegistry {
+ private VirtualNetworkProvider provider;
+
+ @Override
+ public VirtualNetworkProviderService register(VirtualNetworkProvider theProvider) {
+ this.provider = theProvider;
+ return new TestVirtualNetworkProviderService(theProvider);
+ }
+
+ @Override
+ public void unregister(VirtualNetworkProvider theProvider) {
+ this.provider = null;
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+ }
+
+
+ /**
+ * Virtual network provider service implementation for this test class.
+ */
+ private class TestVirtualNetworkProviderService
+ extends AbstractProviderService<VirtualNetworkProvider>
+ implements VirtualNetworkProviderService {
+
+ /**
+ * Constructor.
+ *
+ * @param provider virtual network test provider
+ */
+ protected TestVirtualNetworkProviderService(VirtualNetworkProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void topologyChanged(Set<Set<ConnectPoint>> theClusters) {
+ clusters = theClusters;
+ changed.release();
+ }
+
+ @Override
+ public void tunnelUp(NetworkId networkId, ConnectPoint src,
+ ConnectPoint dst, TunnelId tunnelId) {
+ }
+
+ @Override
+ public void tunnelDown(NetworkId networkId, ConnectPoint src,
+ ConnectPoint dst, TunnelId tunnelId) {
+ }
+ }
+
+ /**
+ * Core service test class.
+ */
+ private class TestCoreService extends CoreServiceAdapter {
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ return new IdGenerator() {
+ private AtomicLong counter = new AtomicLong(0);
+
+ @Override
+ public long getNewId() {
+ return counter.getAndIncrement();
+ }
+ };
+ }
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
new file mode 100644
index 0000000..90e1664
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.impl.provider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+import static org.junit.Assert.assertEquals;
+
+public class VirtualProviderManagerTest {
+
+ private static final String TEST_SCHEME1 = "test1";
+ private static final String TEST_SCHEME2 = "test2";
+ private static final String TEST_ID1 = "org.onosproject.virtual.testprovider1";
+ private static final String TEST_ID2 = "org.onosproject.virtual.testprovider1";
+ private static final NetworkId NETWORK_ID1 = NetworkId.networkId(1);
+ private static final NetworkId NETWORK_ID2 = NetworkId.networkId(2);
+
+ VirtualProviderManager virtualProviderManager;
+
+ @Before
+ public void setUp() throws Exception {
+ virtualProviderManager = new VirtualProviderManager();
+ }
+
+ /**
+ * Tests registerProvider() and unregisterProvider().
+ */
+ @Test
+ public void registerProviderTest() {
+ TestProvider1 provider1 = new TestProvider1();
+ virtualProviderManager.registerProvider(provider1);
+
+ assertEquals("The number of registered provider did not match.", 1,
+ virtualProviderManager.getProviders().size());
+
+ assertEquals("The registered provider did not match", provider1,
+ virtualProviderManager.getProvider(TEST_SCHEME1));
+
+ virtualProviderManager.unregisterProvider(provider1);
+
+ TestProvider2 provider2 = new TestProvider2();
+ virtualProviderManager.registerProvider(provider2);
+
+ assertEquals("The number of registered provider did not match.", 1,
+ virtualProviderManager.getProviders().size());
+
+ virtualProviderManager.unregisterProvider(provider2);
+
+ assertEquals("The number of registered provider did not match.", 0,
+ virtualProviderManager.getProviders().size());
+ }
+
+ /**
+ * Tests registerProviderService() and getProviderService().
+ */
+ @Test
+ public void registerProviderServiceTest() {
+ TestProvider1 provider1 = new TestProvider1();
+ virtualProviderManager.registerProvider(provider1);
+
+ TestProviderService1 providerService1 = new TestProviderService1();
+ virtualProviderManager.registerProviderService(NETWORK_ID1, providerService1);
+
+ assertEquals(providerService1,
+ virtualProviderManager.getProviderService(NETWORK_ID1, TestProvider1.class));
+ }
+
+ private class TestProvider1 extends AbstractVirtualProvider {
+ protected TestProvider1() {
+ super(new ProviderId(TEST_SCHEME1, TEST_ID1));
+ }
+ }
+
+ private class TestProvider2 extends AbstractVirtualProvider {
+ protected TestProvider2() {
+ super(new ProviderId(TEST_SCHEME2, TEST_ID2));
+ }
+ }
+
+ private class TestProviderService1 extends AbstractVirtualProviderService<TestProvider1> {
+ }
+
+ private class TestProviderService2 extends AbstractVirtualProviderService<TestProvider2> {
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/TenantWebResourceTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/TenantWebResourceTest.java
new file mode 100644
index 0000000..def32c5
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/TenantWebResourceTest.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2018-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.rest;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableSet;
+import org.glassfish.jersey.client.ClientProperties;
+import org.hamcrest.Description;
+import org.hamcrest.Matchers;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.rest.resources.ResourceTest;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit tests for tenant REST APIs.
+ */
+@Ignore
+public class TenantWebResourceTest extends ResourceTest {
+
+ private final VirtualNetworkAdminService mockVnetAdminService = createMock(VirtualNetworkAdminService.class);
+
+ final HashSet<TenantId> tenantIdSet = new HashSet<>();
+
+ private static final String ID = "id";
+
+ private final TenantId tenantId1 = TenantId.tenantId("TenantId1");
+ private final TenantId tenantId2 = TenantId.tenantId("TenantId2");
+ private final TenantId tenantId3 = TenantId.tenantId("TenantId3");
+ private final TenantId tenantId4 = TenantId.tenantId("TenantId4");
+
+ /**
+ * Sets up the global values for all the tests.
+ */
+ @Before
+ public void setUpTest() {
+ // Register the services needed for the test
+ CodecManager codecService = new CodecManager();
+ codecService.activate();
+ ServiceDirectory testDirectory =
+ new TestServiceDirectory()
+ .add(VirtualNetworkAdminService.class, mockVnetAdminService)
+ .add(CodecService.class, codecService);
+
+ setServiceDirectory(testDirectory);
+ }
+
+ /**
+ * Hamcrest matcher to check that a tenant id representation in JSON matches
+ * the actual tenant id.
+ */
+ public static class TenantIdJsonMatcher extends TypeSafeMatcher<JsonObject> {
+ private final TenantId tenantId;
+ private String reason = "";
+
+ public TenantIdJsonMatcher(TenantId tenantIdValue) {
+ tenantId = tenantIdValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonObject jsonHost) {
+ // Check the tenant id
+ final String jsonId = jsonHost.get(ID).asString();
+ if (!jsonId.equals(tenantId.id())) {
+ reason = ID + " " + tenantId.id();
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a tenant id array matcher.
+ *
+ * @param tenantId tenant id object we are looking for
+ * @return matcher
+ */
+ private static TenantIdJsonMatcher matchesTenantId(TenantId tenantId) {
+ return new TenantIdJsonMatcher(tenantId);
+ }
+
+ /**
+ * Hamcrest matcher to check that a tenant id is represented properly in a JSON
+ * array of tenant ids.
+ */
+ public static class TenantIdJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+ private final TenantId tenantId;
+ private String reason = "";
+
+ public TenantIdJsonArrayMatcher(TenantId tenantIdValue) {
+ tenantId = tenantIdValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonArray json) {
+ boolean tenantIdFound = false;
+ final int expectedAttributes = 1;
+ for (int tenantIdIndex = 0; tenantIdIndex < json.size();
+ tenantIdIndex++) {
+
+ final JsonObject jsonHost = json.get(tenantIdIndex).asObject();
+
+ // Only 1 attribute - ID.
+ if (jsonHost.names().size() < expectedAttributes) {
+ reason = "Found a tenant id with the wrong number of attributes";
+ return false;
+ }
+
+ final String jsonDeviceKeyId = jsonHost.get(ID).asString();
+ if (jsonDeviceKeyId.equals(tenantId.id())) {
+ tenantIdFound = true;
+
+ // We found the correct tenant id, check the tenant id attribute values
+ assertThat(jsonHost, matchesTenantId(tenantId));
+ }
+ }
+ if (!tenantIdFound) {
+ reason = "Tenant id " + tenantId.id() + " was not found";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a tenant id array matcher.
+ *
+ * @param tenantId tenant id object we are looking for
+ * @return matcher
+ */
+ private static TenantIdJsonArrayMatcher hasTenantId(TenantId tenantId) {
+ return new TenantIdJsonArrayMatcher(tenantId);
+ }
+
+ /**
+ * Tests the result of the REST API GET when there are no tenant ids.
+ */
+ @Test
+ public void testGetTenantsEmptyArray() {
+ expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ String response = wt.path("tenants").request().get(String.class);
+ assertThat(response, is("{\"tenants\":[]}"));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when tenant ids are defined.
+ */
+ @Test
+ public void testGetTenantIdsArray() {
+ tenantIdSet.add(tenantId1);
+ tenantIdSet.add(tenantId2);
+ tenantIdSet.add(tenantId3);
+ tenantIdSet.add(tenantId4);
+ expect(mockVnetAdminService.getTenantIds()).andReturn(tenantIdSet).anyTimes();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ String response = wt.path("tenants").request().get(String.class);
+ assertThat(response, containsString("{\"tenants\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("tenants"));
+
+ final JsonArray tenantIds = result.get("tenants").asArray();
+ assertThat(tenantIds, notNullValue());
+ assertEquals("Device keys array is not the correct size.",
+ tenantIdSet.size(), tenantIds.size());
+
+ tenantIdSet.forEach(tenantId -> assertThat(tenantIds, hasTenantId(tenantId)));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of new tenant id using POST via JSON stream.
+ */
+ @Test
+ public void testPost() {
+ mockVnetAdminService.registerTenantId(anyObject());
+ tenantIdSet.add(tenantId2);
+ expect(mockVnetAdminService.getTenantIds()).andReturn(tenantIdSet).anyTimes();
+ expectLastCall();
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = TenantWebResourceTest.class
+ .getResourceAsStream("post-tenant.json");
+
+ Response response = wt.path("tenants").request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ String location = response.getLocation().getPath();
+ assertThat(location, Matchers.startsWith("/tenants/" + tenantId2));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null tenant id using POST via JSON stream.
+ */
+ @Test
+ public void testPostNullTenantId() {
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ String response = wt.path("tenants")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null tenant id did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a tenant id with DELETE request.
+ */
+ @Test
+ public void testDelete() {
+ expect(mockVnetAdminService.getTenantIds())
+ .andReturn(ImmutableSet.of(tenantId2)).anyTimes();
+ mockVnetAdminService.unregisterTenantId(anyObject());
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ Response response = wt.path("tenants/" + tenantId2)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .delete();
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests that a DELETE of a non-existent tenant id throws an exception.
+ */
+ @Test
+ public void testDeleteNonExistentDeviceKey() {
+ expect(mockVnetAdminService.getTenantIds())
+ .andReturn(ImmutableSet.of())
+ .anyTimes();
+ expectLastCall();
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+
+ try {
+ wt.path("tenants/" + "NON_EXISTENT_TENANT_ID")
+ .request()
+ .delete(String.class);
+ fail("Delete of a non-existent tenant did not throw an exception");
+ } catch (NotFoundException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResourceTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResourceTest.java
new file mode 100644
index 0000000..3b1bd51
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/rest/VirtualNetworkWebResourceTest.java
@@ -0,0 +1,1270 @@
+/*
+ * Copyright 2018-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.rest;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.glassfish.jersey.client.ClientProperties;
+import org.hamcrest.Description;
+import org.hamcrest.Matchers;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualHost;
+import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualHost;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+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.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.PortNumber;
+import org.onosproject.rest.resources.HostResourceTest;
+import org.onosproject.rest.resources.LinksResourceTest;
+import org.onosproject.rest.resources.ResourceTest;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.onosproject.net.PortNumber.portNumber;
+
+/**
+ * Unit tests for virtual network REST APIs.
+ */
+@Ignore
+public class VirtualNetworkWebResourceTest extends ResourceTest {
+
+ private final VirtualNetworkAdminService mockVnetAdminService = createMock(VirtualNetworkAdminService.class);
+ private final VirtualNetworkService mockVnetService = createMock(VirtualNetworkService.class);
+ private CodecManager codecService;
+
+ private final HashSet<VirtualDevice> vdevSet = new HashSet<>();
+ private final HashSet<VirtualPort> vportSet = new HashSet<>();
+
+ private static final String ID = "networkId";
+ private static final String TENANT_ID = "tenantId";
+ private static final String DEVICE_ID = "deviceId";
+ private static final String PORT_NUM = "portNum";
+ private static final String PHYS_DEVICE_ID = "physDeviceId";
+ private static final String PHYS_PORT_NUM = "physPortNum";
+
+ private final TenantId tenantId2 = TenantId.tenantId("TenantId2");
+ private final TenantId tenantId3 = TenantId.tenantId("TenantId3");
+ private final TenantId tenantId4 = TenantId.tenantId("TenantId4");
+
+ private final NetworkId networkId1 = NetworkId.networkId(1);
+ private final NetworkId networkId2 = NetworkId.networkId(2);
+ private final NetworkId networkId3 = NetworkId.networkId(3);
+ private final NetworkId networkId4 = NetworkId.networkId(4);
+
+ private final VirtualNetwork vnet1 = new DefaultVirtualNetwork(networkId1, tenantId3);
+ private final VirtualNetwork vnet2 = new DefaultVirtualNetwork(networkId2, tenantId3);
+ private final VirtualNetwork vnet3 = new DefaultVirtualNetwork(networkId3, tenantId3);
+ private final VirtualNetwork vnet4 = new DefaultVirtualNetwork(networkId4, tenantId3);
+
+ private final DeviceId devId1 = DeviceId.deviceId("devid1");
+ private final DeviceId devId2 = DeviceId.deviceId("devid2");
+ private final DeviceId devId22 = DeviceId.deviceId("dev22");
+
+ private final VirtualDevice vdev1 = new DefaultVirtualDevice(networkId3, devId1);
+ private final VirtualDevice vdev2 = new DefaultVirtualDevice(networkId3, devId2);
+
+ private final Device dev1 = NetTestTools.device("dev1");
+ private final Device dev2 = NetTestTools.device("dev2");
+ private final Device dev22 = NetTestTools.device("dev22");
+
+ private final ConnectPoint cp1 = new ConnectPoint(dev1.id(), portNumber(1));
+ private final ConnectPoint cp2 = new ConnectPoint(dev2.id(), portNumber(2));
+
+ private final VirtualPort vport22 = new DefaultVirtualPort(networkId3,
+ dev22, portNumber(22), cp1);
+ private final VirtualPort vport23 = new DefaultVirtualPort(networkId3,
+ dev22, portNumber(23), cp2);
+
+ private final ConnectPoint cp11 = NetTestTools.connectPoint(devId1.toString(), 21);
+ private final ConnectPoint cp21 = NetTestTools.connectPoint(devId2.toString(), 22);
+ private final ConnectPoint cp12 = NetTestTools.connectPoint(devId1.toString(), 2);
+ private final ConnectPoint cp22 = NetTestTools.connectPoint(devId2.toString(), 22);
+
+ private final VirtualLink vlink1 = DefaultVirtualLink.builder()
+ .networkId(networkId3)
+ .src(cp22)
+ .dst(cp11)
+ .build();
+
+ private final VirtualLink vlink2 = DefaultVirtualLink.builder()
+ .networkId(networkId3)
+ .src(cp12)
+ .dst(cp21)
+ .build();
+
+ private final MacAddress mac1 = MacAddress.valueOf("00:11:00:00:00:01");
+ private final MacAddress mac2 = MacAddress.valueOf("00:22:00:00:00:02");
+ private final VlanId vlan1 = VlanId.vlanId((short) 11);
+ private final VlanId vlan2 = VlanId.vlanId((short) 22);
+ private final IpAddress ip1 = IpAddress.valueOf("10.0.0.1");
+ private final IpAddress ip2 = IpAddress.valueOf("10.0.0.2");
+ private final IpAddress ip3 = IpAddress.valueOf("10.0.0.3");
+
+ private final HostId hId1 = HostId.hostId(mac1, vlan1);
+ private final HostId hId2 = HostId.hostId(mac2, vlan2);
+ private final HostLocation loc1 = new HostLocation(devId1, portNumber(100), 123L);
+ private final HostLocation loc2 = new HostLocation(devId2, portNumber(200), 123L);
+ private final Set<IpAddress> ipSet1 = Sets.newHashSet(ip1, ip2);
+ private final Set<IpAddress> ipSet2 = Sets.newHashSet(ip1, ip3);
+ private final VirtualHost vhost1 = new DefaultVirtualHost(networkId1, hId1,
+ mac1, vlan1, loc1, ipSet1);
+ private final VirtualHost vhost2 = new DefaultVirtualHost(networkId2, hId2,
+ mac2, vlan2, loc2, ipSet2);
+
+
+
+
+ /**
+ * Sets up the global values for all the tests.
+ */
+ @Before
+ public void setUpTest() {
+ // Register the services needed for the test
+ codecService = new CodecManager();
+ codecService.activate();
+ ServiceDirectory testDirectory =
+ new TestServiceDirectory()
+ .add(VirtualNetworkAdminService.class, mockVnetAdminService)
+ .add(VirtualNetworkService.class, mockVnetService)
+ .add(CodecService.class, codecService);
+
+ setServiceDirectory(testDirectory);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual network entity representation in JSON matches
+ * the actual virtual network entity.
+ */
+ private static final class JsonObjectMatcher<T> extends TypeSafeMatcher<JsonObject> {
+ private final T vnetEntity;
+ private List<String> jsonFieldNames;
+ private String reason = "";
+ private BiFunction<T, String, String> getValue; // get vnetEntity's value
+
+ private JsonObjectMatcher(T vnetEntityValue,
+ List<String> jsonFieldNames1,
+ BiFunction<T, String, String> getValue1) {
+ vnetEntity = vnetEntityValue;
+ jsonFieldNames = jsonFieldNames1;
+ getValue = getValue1;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonObject jsonHost) {
+ return jsonFieldNames
+ .stream()
+ .allMatch(s -> checkField(jsonHost, s, getValue.apply(vnetEntity, s)));
+ }
+
+ private boolean checkField(JsonObject jsonHost, String jsonFieldName,
+ String objectValue) {
+ final String jsonValue = jsonHost.get(jsonFieldName).asString();
+ if (!jsonValue.equals(objectValue)) {
+ reason = jsonFieldName + " " + objectValue;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual network id array matcher.
+ *
+ * @param obj virtual network id object we are looking for
+ * @return matcher
+ */
+ /**
+ * Factory to allocate a virtual network entity matcher.
+ *
+ * @param obj virtual network object we are looking for
+ * @param jsonFieldNames JSON field names to check against
+ * @param getValue function to retrieve value from virtual network object
+ * @param <T> the type of virtual network object
+ * @return matcher
+ */
+ private static <T> JsonObjectMatcher matchesVnetEntity(T obj, List<String> jsonFieldNames,
+ BiFunction<T, String, String> getValue) {
+ return new JsonObjectMatcher<T>(obj, jsonFieldNames, getValue);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual network entity is represented properly in a JSON
+ * array of virtual network entities.
+ */
+ protected static class JsonArrayMatcher<T> extends TypeSafeMatcher<JsonArray> {
+ private final T vnetEntity;
+ private String reason = "";
+ private Function<T, String> getKey; // gets vnetEntity's key
+ private BiPredicate<T, JsonObject> checkKey; // check vnetEntity's key with JSON rep'n
+ private List<String> jsonFieldNames; // field/property names
+ private BiFunction<T, String, String> getValue; // get vnetEntity's value
+
+ protected JsonArrayMatcher(T vnetEntityValue, Function<T, String> getKey1,
+ BiPredicate<T, JsonObject> checkKey1,
+ List<String> jsonFieldNames1,
+ BiFunction<T, String, String> getValue1) {
+ vnetEntity = vnetEntityValue;
+ getKey = getKey1;
+ checkKey = checkKey1;
+ jsonFieldNames = jsonFieldNames1;
+ getValue = getValue1;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonArray json) {
+ boolean itemFound = false;
+ final int expectedAttributes = jsonFieldNames.size();
+ for (int jsonArrayIndex = 0; jsonArrayIndex < json.size();
+ jsonArrayIndex++) {
+
+ final JsonObject jsonHost = json.get(jsonArrayIndex).asObject();
+
+ if (jsonHost.names().size() < expectedAttributes) {
+ reason = "Found a virtual network with the wrong number of attributes";
+ return false;
+ }
+
+ if (checkKey != null && checkKey.test(vnetEntity, jsonHost)) {
+ itemFound = true;
+ assertThat(jsonHost, matchesVnetEntity(vnetEntity, jsonFieldNames, getValue));
+ }
+ }
+ if (!itemFound) {
+ reason = getKey.apply(vnetEntity) + " was not found";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Array matcher for VirtualNetwork.
+ */
+ private static final class VnetJsonArrayMatcher extends JsonArrayMatcher<VirtualNetwork> {
+
+ private VnetJsonArrayMatcher(VirtualNetwork vnetIn) {
+ super(vnetIn,
+ vnet -> "Virtual network " + vnet.id().toString(),
+ (vnet, jsonObject) -> jsonObject.get(ID).asString().equals(vnet.id().toString()),
+ ImmutableList.of(ID, TENANT_ID),
+ (vnet, s) -> s.equals(ID) ? vnet.id().toString()
+ : s.equals(TENANT_ID) ? vnet.tenantId().toString()
+ : null
+ );
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual network array matcher.
+ *
+ * @param vnet virtual network object we are looking for
+ * @return matcher
+ */
+ private VnetJsonArrayMatcher hasVnet(VirtualNetwork vnet) {
+ return new VnetJsonArrayMatcher(vnet);
+ }
+
+ // Tests for Virtual Networks
+
+ /**
+ * Tests the result of the REST API GET when there are no virtual networks.
+ */
+ @Test
+ public void testGetVirtualNetworksEmptyArray() {
+ expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetAdminService);
+ expect(mockVnetService.getVirtualNetworks(tenantId4)).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String response = wt.path("vnets").request().get(String.class);
+ assertThat(response, is("{\"vnets\":[]}"));
+
+ verify(mockVnetService);
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when virtual networks are defined.
+ */
+ @Test
+ public void testGetVirtualNetworksArray() {
+ final Set<VirtualNetwork> vnetSet = ImmutableSet.of(vnet1, vnet2, vnet3, vnet4);
+ expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of(tenantId3)).anyTimes();
+ replay(mockVnetAdminService);
+ expect(mockVnetService.getVirtualNetworks(tenantId3)).andReturn(vnetSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String response = wt.path("vnets").request().get(String.class);
+ assertThat(response, containsString("{\"vnets\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("vnets"));
+
+ final JsonArray vnetJsonArray = result.get("vnets").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual networks array is not the correct size.",
+ vnetSet.size(), vnetJsonArray.size());
+
+ vnetSet.forEach(vnet -> assertThat(vnetJsonArray, hasVnet(vnet)));
+
+ verify(mockVnetService);
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests the result of the REST API GET for virtual networks with tenant id.
+ */
+ @Test
+ public void testGetVirtualNetworksByTenantId() {
+ final Set<VirtualNetwork> vnetSet = ImmutableSet.of(vnet1, vnet2, vnet3, vnet4);
+ expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of(tenantId3)).anyTimes();
+ replay(mockVnetAdminService);
+ expect(mockVnetService.getVirtualNetworks(tenantId3)).andReturn(vnetSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String response = wt.path("vnets/" + tenantId3.id()).request().get(String.class);
+ assertThat(response, containsString("{\"vnets\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("vnets"));
+
+ final JsonArray vnetJsonArray = result.get("vnets").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual networks array is not the correct size.",
+ vnetSet.size(), vnetJsonArray.size());
+
+ vnetSet.forEach(vnet -> assertThat(vnetJsonArray, hasVnet(vnet)));
+
+ verify(mockVnetService);
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests the result of the REST API GET for virtual networks with tenant id.
+ */
+ @Test
+ public void testGetVirtualNetworksByNonExistentTenantId() {
+ String tenantIdName = "NON_EXISTENT_TENANT_ID";
+ expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of(tenantId3)).anyTimes();
+ replay(mockVnetAdminService);
+ expect(mockVnetService.getVirtualNetworks(anyObject())).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+
+ try {
+ wt.path("vnets/" + tenantIdName)
+ .request()
+ .get(String.class);
+ fail("Get of a non-existent virtual network did not throw an exception");
+ } catch (NotFoundException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
+ }
+
+ verify(mockVnetService);
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of new virtual network using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualNetwork() {
+ expect(mockVnetAdminService.createVirtualNetwork(tenantId2)).andReturn(vnet1);
+ expectLastCall();
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = TenantWebResourceTest.class
+ .getResourceAsStream("post-tenant.json");
+
+ Response response = wt.path("vnets").request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ String location = response.getLocation().getPath();
+ assertThat(location, Matchers.startsWith("/vnets/" + vnet1.id().toString()));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null virtual network using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualNetworkNullTenantId() {
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ wt.path("vnets")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null virtual network did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a virtual network with DELETE request.
+ */
+ @Test
+ public void testDeleteVirtualNetwork() {
+ mockVnetAdminService.removeVirtualNetwork(anyObject());
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ Response response = wt.path("vnets/" + "2")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .delete();
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests that a DELETE of a non-existent virtual network throws an exception.
+ */
+ @Test
+ public void testDeleteNetworkNonExistentNetworkId() {
+ expect(mockVnetAdminService.getTenantIds())
+ .andReturn(ImmutableSet.of())
+ .anyTimes();
+ expectLastCall();
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+
+ try {
+ wt.path("vnets/" + "NON_EXISTENT_NETWORK_ID")
+ .request()
+ .delete(String.class);
+ fail("Delete of a non-existent virtual network did not throw an exception");
+ } catch (NotFoundException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ // Tests for Virtual Device
+
+ /**
+ * Tests the result of the REST API GET when there are no virtual devices.
+ */
+ @Test
+ public void testGetVirtualDevicesEmptyArray() {
+ NetworkId networkId = networkId4;
+ expect(mockVnetService.getVirtualDevices(networkId)).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/devices";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, is("{\"devices\":[]}"));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when virtual devices are defined.
+ */
+ @Test
+ public void testGetVirtualDevicesArray() {
+ NetworkId networkId = networkId3;
+ vdevSet.add(vdev1);
+ vdevSet.add(vdev2);
+ expect(mockVnetService.getVirtualDevices(networkId)).andReturn(vdevSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/devices";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, containsString("{\"devices\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("devices"));
+
+ final JsonArray vnetJsonArray = result.get("devices").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual devices array is not the correct size.",
+ vdevSet.size(), vnetJsonArray.size());
+
+ vdevSet.forEach(vdev -> assertThat(vnetJsonArray, hasVdev(vdev)));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Array matcher for VirtualDevice.
+ */
+ private static final class VdevJsonArrayMatcher extends JsonArrayMatcher<VirtualDevice> {
+
+ private VdevJsonArrayMatcher(VirtualDevice vdevIn) {
+ super(vdevIn,
+ vdev -> "Virtual device " + vdev.networkId().toString()
+ + " " + vdev.id().toString(),
+ (vdev, jsonObject) -> jsonObject.get(ID).asString().equals(vdev.networkId().toString())
+ && jsonObject.get(DEVICE_ID).asString().equals(vdev.id().toString()),
+ ImmutableList.of(ID, DEVICE_ID),
+ (vdev, s) -> s.equals(ID) ? vdev.networkId().toString()
+ : s.equals(DEVICE_ID) ? vdev.id().toString()
+ : null
+ );
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual device array matcher.
+ *
+ * @param vdev virtual device object we are looking for
+ * @return matcher
+ */
+ private VdevJsonArrayMatcher hasVdev(VirtualDevice vdev) {
+ return new VdevJsonArrayMatcher(vdev);
+ }
+ /**
+ * Tests adding of new virtual device using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualDevice() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = devId2;
+ expect(mockVnetAdminService.createVirtualDevice(networkId, deviceId)).andReturn(vdev2);
+ expectLastCall();
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-device.json");
+ String reqLocation = "vnets/" + networkId.toString() + "/devices";
+ Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ String location = response.getLocation().getPath();
+ assertThat(location, Matchers.startsWith("/" + reqLocation + "/" + vdev2.id().toString()));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null virtual device using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualDeviceNullJsonStream() {
+ NetworkId networkId = networkId3;
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ String reqLocation = "vnets/" + networkId.toString() + "/devices";
+ wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null virtual device did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a virtual device with DELETE request.
+ */
+ @Test
+ public void testDeleteVirtualDevice() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = devId2;
+ mockVnetAdminService.removeVirtualDevice(networkId, deviceId);
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ String reqLocation = "vnets/" + networkId.toString() + "/devices/" + deviceId.toString();
+ Response response = wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .delete();
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+
+ verify(mockVnetAdminService);
+ }
+
+ // Tests for Virtual Ports
+
+ /**
+ * Tests the result of the REST API GET when there are no virtual ports.
+ */
+ @Test
+ public void testGetVirtualPortsEmptyArray() {
+ NetworkId networkId = networkId4;
+ DeviceId deviceId = devId2;
+ expect(mockVnetService.getVirtualPorts(networkId, deviceId))
+ .andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString()
+ + "/devices/" + deviceId.toString() + "/ports";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, is("{\"ports\":[]}"));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when virtual ports are defined.
+ */
+ @Test
+ public void testGetVirtualPortsArray() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = dev22.id();
+ vportSet.add(vport23);
+ vportSet.add(vport22);
+ expect(mockVnetService.getVirtualPorts(networkId, deviceId)).andReturn(vportSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString()
+ + "/devices/" + deviceId.toString() + "/ports";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, containsString("{\"ports\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("ports"));
+
+ final JsonArray vnetJsonArray = result.get("ports").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual ports array is not the correct size.",
+ vportSet.size(), vnetJsonArray.size());
+
+ vportSet.forEach(vport -> assertThat(vnetJsonArray, hasVport(vport)));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Array matcher for VirtualPort.
+ */
+ private static final class VportJsonArrayMatcher extends JsonArrayMatcher<VirtualPort> {
+
+ private VportJsonArrayMatcher(VirtualPort vportIn) {
+ super(vportIn,
+ vport -> "Virtual port " + vport.networkId().toString() + " "
+ + vport.element().id().toString() + " " + vport.number().toString(),
+ (vport, jsonObject) -> jsonObject.get(ID).asString().equals(vport.networkId().toString())
+ && jsonObject.get(PORT_NUM).asString().equals(vport.number().toString())
+ && jsonObject.get(DEVICE_ID).asString().equals(vport.element().id().toString()),
+ ImmutableList.of(ID, DEVICE_ID, PORT_NUM, PHYS_DEVICE_ID, PHYS_PORT_NUM),
+ (vport, s) -> s.equals(ID) ? vport.networkId().toString()
+ : s.equals(DEVICE_ID) ? vport.element().id().toString()
+ : s.equals(PORT_NUM) ? vport.number().toString()
+ : s.equals(PHYS_DEVICE_ID) ? vport.realizedBy().deviceId().toString()
+ : s.equals(PHYS_PORT_NUM) ? vport.realizedBy().port().toString()
+ : null
+ );
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual port array matcher.
+ *
+ * @param vport virtual port object we are looking for
+ * @return matcher
+ */
+ private VportJsonArrayMatcher hasVport(VirtualPort vport) {
+ return new VportJsonArrayMatcher(vport);
+ }
+
+ /**
+ * Tests adding of new virtual port using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualPort() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = devId22;
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+ Device physDevice = new DefaultDevice(null, DeviceId.deviceId("dev1"),
+ null, null, null, null, null, null, annotations);
+ ConnectPoint cp1 = new ConnectPoint(physDevice.id(), portNumber(1));
+ expect(mockVnetAdminService.createVirtualPort(networkId, deviceId, portNumber(22), cp1))
+ .andReturn(vport22);
+
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-port.json");
+ String reqLocation = "vnets/" + networkId.toString()
+ + "/devices/" + deviceId.toString() + "/ports";
+ Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null virtual port using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualPortNullJsonStream() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = devId2;
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ String reqLocation = "vnets/" + networkId.toString()
+ + "/devices/" + deviceId.toString() + "/ports";
+ wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null virtual port did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a virtual port with DELETE request.
+ */
+ @Test
+ public void testDeleteVirtualPort() {
+ NetworkId networkId = networkId3;
+ DeviceId deviceId = devId2;
+ PortNumber portNum = portNumber(2);
+ mockVnetAdminService.removeVirtualPort(networkId, deviceId, portNum);
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ String reqLocation = "vnets/" + networkId.toString()
+ + "/devices/" + deviceId.toString() + "/ports/" + portNum.toLong();
+ Response response = wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .delete();
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+
+ verify(mockVnetAdminService);
+ }
+
+ // Tests for Virtual Links
+
+ /**
+ * Tests the result of the REST API GET when there are no virtual links.
+ */
+ @Test
+ public void testGetVirtualLinksEmptyArray() {
+ NetworkId networkId = networkId4;
+ expect(mockVnetService.getVirtualLinks(networkId)).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/links";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, is("{\"links\":[]}"));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when virtual links are defined.
+ */
+ @Test
+ public void testGetVirtualLinksArray() {
+ NetworkId networkId = networkId3;
+ final Set<VirtualLink> vlinkSet = ImmutableSet.of(vlink1, vlink2);
+ expect(mockVnetService.getVirtualLinks(networkId)).andReturn(vlinkSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/links";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, containsString("{\"links\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("links"));
+
+ final JsonArray vnetJsonArray = result.get("links").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual links array is not the correct size.",
+ vlinkSet.size(), vnetJsonArray.size());
+
+ vlinkSet.forEach(vlink -> assertThat(vnetJsonArray, hasVlink(vlink)));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual link representation in JSON matches
+ * the actual virtual link.
+ */
+ private static final class VirtualLinkJsonMatcher extends LinksResourceTest.LinkJsonMatcher {
+ private final VirtualLink vlink;
+ private String reason = "";
+
+ private VirtualLinkJsonMatcher(VirtualLink vlinkValue) {
+ super(vlinkValue);
+ vlink = vlinkValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonObject jsonLink) {
+ if (!super.matchesSafely(jsonLink)) {
+ return false;
+ }
+ // check NetworkId
+ String jsonNetworkId = jsonLink.get(ID).asString();
+ String networkId = vlink.networkId().toString();
+ if (!jsonNetworkId.equals(networkId)) {
+ reason = ID + " was " + jsonNetworkId;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual link matcher.
+ *
+ * @param vlink virtual link object we are looking for
+ * @return matcher
+ */
+ private static VirtualLinkJsonMatcher matchesVirtualLink(VirtualLink vlink) {
+ return new VirtualLinkJsonMatcher(vlink);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual link is represented properly in a JSON
+ * array of links.
+ */
+ private static final class VirtualLinkJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+ private final VirtualLink vlink;
+ private String reason = "";
+
+ private VirtualLinkJsonArrayMatcher(VirtualLink vlinkValue) {
+ vlink = vlinkValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonArray json) {
+ for (int jsonLinkIndex = 0; jsonLinkIndex < json.size();
+ jsonLinkIndex++) {
+
+ JsonObject jsonLink = json.get(jsonLinkIndex).asObject();
+
+ if (matchesVirtualLink(vlink).matchesSafely(jsonLink)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual link array matcher.
+ *
+ * @param vlink virtual link object we are looking for
+ * @return matcher
+ */
+ private VirtualLinkJsonArrayMatcher hasVlink(VirtualLink vlink) {
+ return new VirtualLinkJsonArrayMatcher(vlink);
+ }
+
+ /**
+ * Tests adding of new virtual link using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualLink() {
+ NetworkId networkId = networkId3;
+ expect(mockVnetAdminService.createVirtualLink(networkId, cp22, cp11))
+ .andReturn(vlink1);
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-link.json");
+ String reqLocation = "vnets/" + networkId.toString() + "/links";
+ Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ String location = response.getLocation().getPath();
+ assertThat(location, Matchers.startsWith("/" + reqLocation));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null virtual link using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualLinkNullJsonStream() {
+ NetworkId networkId = networkId3;
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ String reqLocation = "vnets/" + networkId.toString() + "/links";
+ wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null virtual link did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a virtual link with DELETE request.
+ */
+ @Test
+ public void testDeleteVirtualLink() {
+ NetworkId networkId = networkId3;
+ mockVnetAdminService.removeVirtualLink(networkId, cp22, cp11);
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-link.json");
+ String reqLocation = "vnets/" + networkId.toString() + "/links";
+ Response response = wt.path(reqLocation).request().method("DELETE", Entity.json(jsonStream));
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+ verify(mockVnetAdminService);
+ }
+
+ // Tests for Virtual Hosts
+
+ /**
+ * Tests the result of the REST API GET when there are no virtual hosts.
+ */
+ @Test
+ public void testGetVirtualHostsEmptyArray() {
+ NetworkId networkId = networkId4;
+ expect(mockVnetService.getVirtualHosts(networkId)).andReturn(ImmutableSet.of()).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/hosts";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, is("{\"hosts\":[]}"));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Tests the result of the REST API GET when virtual hosts are defined.
+ */
+ @Test
+ public void testGetVirtualHostsArray() {
+ NetworkId networkId = networkId3;
+ final Set<VirtualHost> vhostSet = ImmutableSet.of(vhost1, vhost2);
+ expect(mockVnetService.getVirtualHosts(networkId)).andReturn(vhostSet).anyTimes();
+ replay(mockVnetService);
+
+ WebTarget wt = target();
+ String location = "vnets/" + networkId.toString() + "/hosts";
+ String response = wt.path(location).request().get(String.class);
+ assertThat(response, containsString("{\"hosts\":["));
+
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result, notNullValue());
+
+ assertThat(result.names(), hasSize(1));
+ assertThat(result.names().get(0), is("hosts"));
+
+ final JsonArray vnetJsonArray = result.get("hosts").asArray();
+ assertThat(vnetJsonArray, notNullValue());
+ assertEquals("Virtual hosts array is not the correct size.",
+ vhostSet.size(), vnetJsonArray.size());
+
+ vhostSet.forEach(vhost -> assertThat(vnetJsonArray, hasVhost(vhost)));
+
+ verify(mockVnetService);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual host representation in JSON matches
+ * the actual virtual host.
+ */
+ private static final class VirtualHostJsonMatcher extends HostResourceTest.HostJsonMatcher {
+ private final VirtualHost vhost;
+ private String reason = "";
+
+ private VirtualHostJsonMatcher(VirtualHost vhostValue) {
+ super(vhostValue);
+ vhost = vhostValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonObject jsonHost) {
+ if (!super.matchesSafely(jsonHost)) {
+ return false;
+ }
+ // check NetworkId
+ String jsonNetworkId = jsonHost.get(ID).asString();
+ String networkId = vhost.networkId().toString();
+ if (!jsonNetworkId.equals(networkId)) {
+ reason = ID + " was " + jsonNetworkId;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual host matcher.
+ *
+ * @param vhost virtual host object we are looking for
+ * @return matcher
+ */
+ private static VirtualHostJsonMatcher matchesVirtualHost(VirtualHost vhost) {
+ return new VirtualHostJsonMatcher(vhost);
+ }
+
+ /**
+ * Hamcrest matcher to check that a virtual host is represented properly in a JSON
+ * array of hosts.
+ */
+ private static final class VirtualHostJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+ private final VirtualHost vhost;
+ private String reason = "";
+
+ private VirtualHostJsonArrayMatcher(VirtualHost vhostValue) {
+ vhost = vhostValue;
+ }
+
+ @Override
+ public boolean matchesSafely(JsonArray json) {
+ for (int jsonHostIndex = 0; jsonHostIndex < json.size();
+ jsonHostIndex++) {
+
+ JsonObject jsonHost = json.get(jsonHostIndex).asObject();
+
+ if (matchesVirtualHost(vhost).matchesSafely(jsonHost)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(reason);
+ }
+ }
+
+ /**
+ * Factory to allocate a virtual host array matcher.
+ *
+ * @param vhost virtual host object we are looking for
+ * @return matcher
+ */
+ private VirtualHostJsonArrayMatcher hasVhost(VirtualHost vhost) {
+ return new VirtualHostJsonArrayMatcher(vhost);
+ }
+
+ /**
+ * Tests adding of new virtual host using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualHost() {
+ NetworkId networkId = networkId3;
+ expect(mockVnetAdminService.createVirtualHost(networkId, hId1, mac1, vlan1, loc1, ipSet1))
+ .andReturn(vhost1);
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-host.json");
+ String reqLocation = "vnets/" + networkId.toString() + "/hosts";
+ Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(jsonStream));
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+
+ String location = response.getLocation().getPath();
+ assertThat(location, Matchers.startsWith("/" + reqLocation));
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests adding of a null virtual host using POST via JSON stream.
+ */
+ @Test
+ public void testPostVirtualHostNullJsonStream() {
+ NetworkId networkId = networkId3;
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target();
+ try {
+ String reqLocation = "vnets/" + networkId.toString() + "/hosts";
+ wt.path(reqLocation)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.json(null), String.class);
+ fail("POST of null virtual host did not throw an exception");
+ } catch (BadRequestException ex) {
+ assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
+ }
+
+ verify(mockVnetAdminService);
+ }
+
+ /**
+ * Tests removing a virtual host with DELETE request.
+ */
+ @Test
+ public void testDeleteVirtualHost() {
+ NetworkId networkId = networkId3;
+ mockVnetAdminService.removeVirtualHost(networkId, hId1);
+ expectLastCall();
+ replay(mockVnetAdminService);
+
+ WebTarget wt = target()
+ .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
+ InputStream jsonStream = VirtualNetworkWebResourceTest.class
+ .getResourceAsStream("post-virtual-host.json");
+ String reqLocation = "vnets/" + networkId.toString() + "/hosts";
+ Response response = wt.path(reqLocation).request().method("DELETE", Entity.json(jsonStream));
+
+ assertThat(response.getStatus(), is(HttpURLConnection.HTTP_NO_CONTENT));
+ verify(mockVnetAdminService);
+ }
+}
diff --git a/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStoreTest.java b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStoreTest.java
new file mode 100644
index 0000000..58d3506
--- /dev/null
+++ b/apps/virtual/app/src/test/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualMastershipStoreTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.store.impl;
+
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.net.DeviceId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.net.MastershipRole.NONE;
+import static org.onosproject.net.MastershipRole.STANDBY;
+
+public class SimpleVirtualMastershipStoreTest {
+
+ private static final NetworkId VNID1 = NetworkId.networkId(1);
+
+ private static final DeviceId VDID1 = DeviceId.deviceId("of:01");
+ private static final DeviceId VDID2 = DeviceId.deviceId("of:02");
+ private static final DeviceId VDID3 = DeviceId.deviceId("of:03");
+ private static final DeviceId VDID4 = DeviceId.deviceId("of:04");
+
+ private static final NodeId N1 = new NodeId("local");
+ private static final NodeId N2 = new NodeId("other");
+
+ private SimpleVirtualMastershipStore sms;
+
+ @Before
+ public void setUp() throws Exception {
+ sms = new SimpleVirtualMastershipStore();
+ sms.activate();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sms.deactivate();
+ }
+
+ @Test
+ public void getRole() {
+ //special case, no backup or master
+ put(VNID1, VDID1, N1, false, false);
+ assertEquals("wrong role", NONE, sms.getRole(VNID1, N1, VDID1));
+
+ //backup exists but we aren't mapped
+ put(VNID1, VDID2, N1, false, true);
+ assertEquals("wrong role", STANDBY, sms.getRole(VNID1, N1, VDID2));
+
+ //N2 is master
+ put(VNID1, VDID3, N2, true, true);
+ assertEquals("wrong role", MASTER, sms.getRole(VNID1, N2, VDID3));
+
+ //N2 is master but N1 is only in backups set
+ put(VNID1, VDID4, N1, false, true);
+ put(VNID1, VDID4, N2, true, false);
+ assertEquals("wrong role", STANDBY, sms.getRole(VNID1, N1, VDID4));
+ }
+
+ @Test
+ public void getMaster() {
+ put(VNID1, VDID3, N2, true, true);
+ assertEquals("wrong role", MASTER, sms.getRole(VNID1, N2, VDID3));
+ assertEquals("wrong node", N2, sms.getMaster(VNID1, VDID3));
+ }
+
+ @Test
+ public void setMaster() {
+ put(VNID1, VDID1, N1, false, false);
+ assertEquals("wrong event", MASTER_CHANGED,
+ Futures.getUnchecked(sms.setMaster(VNID1, N1, VDID1)).type());
+ assertEquals("wrong role", MASTER, sms.getRole(VNID1, N1, VDID1));
+ //set node that's already master - should be ignored
+ assertNull("wrong event",
+ Futures.getUnchecked(sms.setMaster(VNID1, N1, VDID1)));
+
+ //set STANDBY to MASTER
+ put(VNID1, VDID2, N1, false, true);
+ assertEquals("wrong role", STANDBY, sms.getRole(VNID1, N1, VDID2));
+ assertEquals("wrong event", MASTER_CHANGED,
+ Futures.getUnchecked(sms.setMaster(VNID1, N1, VDID2)).type());
+ assertEquals("wrong role", MASTER, sms.getRole(VNID1, N1, VDID2));
+ }
+
+ @Test
+ public void getDevices() {
+ Set<DeviceId> d = Sets.newHashSet(VDID1, VDID2);
+
+ put(VNID1, VDID1, N2, true, true);
+ put(VNID1, VDID2, N2, true, true);
+ put(VNID1, VDID3, N1, true, true);
+ assertTrue("wrong devices", d.equals(sms.getDevices(VNID1, N2)));
+ }
+
+ @Test
+ public void getTermFor() {
+ put(VNID1, VDID1, N1, true, true);
+ assertEquals("wrong term", MastershipTerm.of(N1, 0),
+ sms.getTermFor(VNID1, VDID1));
+
+ //switch to N2 and back - 2 term switches
+ sms.setMaster(VNID1, N2, VDID1);
+ sms.setMaster(VNID1, N1, VDID1);
+ assertEquals("wrong term", MastershipTerm.of(N1, 2),
+ sms.getTermFor(VNID1, VDID1));
+ }
+
+ @Test
+ public void requestRole() {
+ //NONE - become MASTER
+ put(VNID1, VDID1, N1, false, false);
+ assertEquals("wrong role", MASTER,
+ Futures.getUnchecked(sms.requestRole(VNID1, VDID1)));
+
+ //was STANDBY - become MASTER
+ put(VNID1, VDID2, N1, false, true);
+ assertEquals("wrong role", MASTER,
+ Futures.getUnchecked(sms.requestRole(VNID1, VDID2)));
+
+ //other MASTER - stay STANDBY
+ put(VNID1, VDID3, N2, true, false);
+ assertEquals("wrong role", STANDBY,
+ Futures.getUnchecked(sms.requestRole(VNID1, VDID3)));
+
+ //local (N1) is MASTER - stay MASTER
+ put(VNID1, VDID4, N1, true, true);
+ assertEquals("wrong role", MASTER,
+ Futures.getUnchecked(sms.requestRole(VNID1, VDID4)));
+ }
+
+ @Test
+ public void unsetMaster() {
+ //NONE - record backup but take no other action
+ put(VNID1, VDID1, N1, false, false);
+ sms.setStandby(VNID1, N1, VDID1);
+ assertTrue("not backed up", sms.backupsByNetwork.get(VNID1)
+ .get(VDID1).contains(N1));
+ int prev = sms.termMapByNetwork.get(VNID1).get(VDID1).get();
+ sms.setStandby(VNID1, N1, VDID1);
+ assertEquals("term should not change", prev, sms.termMapByNetwork.get(VNID1)
+ .get(VDID1).get());
+
+ //no backup, MASTER
+ put(VNID1, VDID1, N1, true, false);
+ assertNull("expect no MASTER event",
+ Futures.getUnchecked(sms.setStandby(VNID1, N1, VDID1)).roleInfo().master());
+ assertNull("wrong node", sms.masterMapByNetwork.get(VNID1).get(VDID1));
+
+ //backup, switch
+ sms.masterMapByNetwork.get(VNID1).clear();
+ put(VNID1, VDID1, N1, true, true);
+ put(VNID1, VDID1, N2, false, true);
+ put(VNID1, VDID2, N2, true, true);
+ MastershipEvent event = Futures.getUnchecked(sms.setStandby(VNID1, N1, VDID1));
+ assertEquals("wrong event", MASTER_CHANGED, event.type());
+ assertEquals("wrong master", N2, event.roleInfo().master());
+ }
+
+ //helper to populate master/backup structures
+ private void put(NetworkId networkId, DeviceId dev, NodeId node,
+ boolean master, boolean backup) {
+ if (master) {
+ sms.masterMapByNetwork
+ .computeIfAbsent(networkId, k -> new HashMap<>())
+ .put(dev, node);
+ } else if (backup) {
+ List<NodeId> stbys = sms.backupsByNetwork
+ .computeIfAbsent(networkId, k -> new HashMap<>())
+ .getOrDefault(dev, new ArrayList<>());
+ stbys.add(node);
+ sms.backupsByNetwork.get(networkId).put(dev, stbys);
+ }
+
+ sms.termMapByNetwork
+ .computeIfAbsent(networkId, k -> new HashMap<>())
+ .put(dev, new AtomicInteger());
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/resources/domain-config.json b/apps/virtual/app/src/test/resources/domain-config.json
new file mode 100644
index 0000000..beda11a
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/domain-config.json
@@ -0,0 +1,36 @@
+{
+ "domains" : {
+ "cord" : {
+ "basic" : {
+ "name" : "Core Fabric",
+ "applicationName" : "org.onosproject.testdomain",
+ "internalDevices" : [ "of:1" ],
+ "edgePorts" : [ "of:12/1", "of:14/1" ]
+ }
+ },
+ "mpls" : {
+ "basic" : {
+ "name" : "MPLS Core",
+ "applicationName" : "org.onosproject.testdomain",
+ "internalDevices" : [ "of:2" ],
+ "edgePorts" : [ "of:12/2", "of:23/2" ]
+ }
+ },
+ "dc" : {
+ "basic" : {
+ "name" : "Data Center Fabric",
+ "applicationName" : "org.onosproject.testdomain",
+ "internalDevices" : [ "of:3" ],
+ "edgePorts" : [ "of:23/3", "of:34/3" ]
+ }
+ },
+ "optical" : {
+ "basic" : {
+ "name" : "Optical Core",
+ "applicationName" : "org.onosproject.testdomain",
+ "internalDevices" : [ "of:4" ],
+ "edgePorts" : [ "of:14/4", "of:34/4" ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/resources/fractal-domain-config.json b/apps/virtual/app/src/test/resources/fractal-domain-config.json
new file mode 100644
index 0000000..521c840
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/fractal-domain-config.json
@@ -0,0 +1,28 @@
+{
+ "domains" : {
+ "domain1" : {
+ "basic" : {
+ "name" : "Domain 1",
+ "applicationName" : "org.onosproject.meshdomain",
+ "internalDevices" : [ "of:0000000000001001", "of:0000000000001002", "of:0000000000001003" ],
+ "edgePorts" : [ "of:0000000000010000/1", "of:0000000003010000/2", "of:0000000002010000/1" ]
+ }
+ },
+ "domain2" : {
+ "basic" : {
+ "name" : "Domain 2",
+ "applicationName" : "org.onosproject.meshdomain",
+ "internalDevices" : [ "of:0000000000002001", "of:0000000000002002", "of:0000000000002003" ],
+ "edgePorts" : [ "of:0000000000020000/1", "of:0000000003020000/1", "of:0000000002010000/2" ]
+ }
+ },
+ "domain3" : {
+ "basic" : {
+ "name" : "Domain 3",
+ "applicationName" : "org.onosproject.meshdomain",
+ "internalDevices" : [ "of:0000000000003001", "of:0000000000003002", "of:0000000000003003" ],
+ "edgePorts" : [ "of:0000000000030000/1", "of:0000000003010000/1", "of:0000000003020000/2" ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-tenant.json b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-tenant.json
new file mode 100644
index 0000000..407f9a2
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-tenant.json
@@ -0,0 +1,3 @@
+{
+ "id": "TenantId2"
+}
diff --git a/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-device.json b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-device.json
new file mode 100644
index 0000000..fbd129c
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-device.json
@@ -0,0 +1,4 @@
+{
+ "networkId": "3",
+ "deviceId": "devId2"
+}
diff --git a/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-host.json b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-host.json
new file mode 100644
index 0000000..557dc32
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-host.json
@@ -0,0 +1,16 @@
+{
+ "networkId": "3",
+ "id": "00:11:00:00:00:01/11",
+ "mac": "00:11:00:00:00:01",
+ "vlan": "11",
+ "locations": [
+ {
+ "elementId": "devid1",
+ "port": "100"
+ }
+ ],
+ "ipAddresses": [
+ "10.0.0.1",
+ "10.0.0.2"
+ ]
+}
diff --git a/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-link.json b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-link.json
new file mode 100644
index 0000000..8f04673
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-link.json
@@ -0,0 +1,13 @@
+{
+ "networkId": "3",
+ "src": {
+ "device": "of:devid2",
+ "port": "22"
+ },
+ "dst": {
+ "device": "of:devid1",
+ "port": "21"
+ },
+ "type": "VIRTUAL",
+ "state": "ACTIVE"
+}
diff --git a/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-port.json b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-port.json
new file mode 100644
index 0000000..1d60842
--- /dev/null
+++ b/apps/virtual/app/src/test/resources/org/onosproject/incubator/net/virtual/rest/post-virtual-port.json
@@ -0,0 +1,7 @@
+{
+ "networkId": "3",
+ "deviceId": "dev22",
+ "portNum": "22",
+ "physDeviceId": "dev1",
+ "physPortNum": "1"
+}