ONOS-4075 - Distributed virtual network store implementation,
and virtual network manager Junit tests.

Change-Id: Ic1f82822c894e3c394aa95df1e76ae59fe218120
diff --git a/core/api/src/test/java/org/onosproject/store/service/DistributedSetTest.java b/core/api/src/test/java/org/onosproject/store/service/DistributedSetTest.java
index 633786d..e8dbeb6 100644
--- a/core/api/src/test/java/org/onosproject/store/service/DistributedSetTest.java
+++ b/core/api/src/test/java/org/onosproject/store/service/DistributedSetTest.java
@@ -64,7 +64,7 @@
     public void basicTests() {
         set1.add("item1");
         assertEquals("The set name should match.", SETNAME, set1.name());
-        assertEquals("The set name should match.", DistributedPrimitive.Type.SET, set1.primitiveType());
+        assertEquals("The set primitive type should match.", DistributedPrimitive.Type.SET, set1.primitiveType());
 
         set1.add("item2");
         set1.add("item3");
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
index e3339a9..06225ff 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
@@ -27,7 +27,7 @@
 /**
  * Default representation of a virtual device.
  */
-public class DefaultVirtualDevice extends DefaultDevice implements VirtualDevice {
+public final class DefaultVirtualDevice extends DefaultDevice implements VirtualDevice {
 
     private static final String VIRTUAL = "virtual";
     private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
@@ -53,7 +53,7 @@
 
     @Override
     public int hashCode() {
-        return 31 * super.hashCode() + networkId.hashCode();
+        return Objects.hash(networkId);
     }
 
     @Override
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
new file mode 100644
index 0000000..89374d4
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualLink.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.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;
+
+/**
+ * Default representation of a virtual link.
+ */
+public final class DefaultVirtualLink extends DefaultLink implements VirtualLink {
+
+    private static final String VIRTUAL = "virtual";
+    private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
+
+    private final NetworkId networkId;
+    private final TunnelId tunnelId;
+
+    /**
+     * Constructor for a default virtual link.
+     *
+     * @param networkId network identifier
+     * @param src       source connection point
+     * @param dst       destination connection point
+     * @param tunnelId  tunnel identifier
+     */
+    public DefaultVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId tunnelId) {
+        super(PID, src, dst, Type.VIRTUAL, 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();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
index c114191..360d385 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
@@ -22,7 +22,7 @@
 /**
  * Default implementation of the virtual network descriptor.
  */
-public class DefaultVirtualNetwork implements VirtualNetwork {
+public final class DefaultVirtualNetwork implements VirtualNetwork {
 
     private final NetworkId id;
     private final TenantId tenantId;
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java
new file mode 100644
index 0000000..ad56efb
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualPort.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.Element;
+import org.onosproject.net.Port;
+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 Port realizedBy;
+
+    public DefaultVirtualPort(NetworkId networkId, Device device, PortNumber portNumber, Port realizedBy) {
+        super((Element) device, portNumber, false, DefaultAnnotations.builder().build());
+        this.networkId = networkId;
+        this.realizedBy = realizedBy;
+    }
+
+    public NetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public Port 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.realizedBy, that.realizedBy);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("networkId", networkId).add("realizedBy", realizedBy).toString();
+    }
+
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
index 01681ef..c83b2c5 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkService.java
@@ -27,6 +27,11 @@
 public interface VirtualNetworkService {
 
     /**
+     * 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.
      *
diff --git a/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java
new file mode 100644
index 0000000..b99e7e6
--- /dev/null
+++ b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualDeviceTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual device model entity.
+ */
+public class DefaultVirtualDeviceTest {
+    final String deviceIdValue1 = "DEVICE_ID1";
+    final String deviceIdValue2 = "DEVICE_ID2";
+
+    /**
+     * 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), DeviceId.deviceId(deviceIdValue1));
+        DefaultVirtualDevice device2 =
+                new DefaultVirtualDevice(NetworkId.networkId(0), DeviceId.deviceId(deviceIdValue1));
+        DefaultVirtualDevice device3 =
+                new DefaultVirtualDevice(NetworkId.networkId(0), DeviceId.deviceId(deviceIdValue2));
+        DefaultVirtualDevice device4 =
+                new DefaultVirtualDevice(NetworkId.networkId(1), DeviceId.deviceId(deviceIdValue1));
+
+        new EqualsTester().addEqualityGroup(device1, device2).addEqualityGroup(device3)
+                .addEqualityGroup(device4).testEquals();
+    }
+}
diff --git a/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java
new file mode 100644
index 0000000..5661cb9
--- /dev/null
+++ b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualLinkTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.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.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual link model entity.
+ */
+public class DefaultVirtualLinkTest {
+    final String deviceIdValue1 = "DEVICE_ID1";
+    final String deviceIdValue2 = "DEVICE_ID2";
+
+    /**
+     * Checks that the DefaultVirtualLink class is immutable.
+     */
+    @Test
+    public void testImmutability() {
+        assertThatClassIsImmutable(DefaultVirtualLink.class);
+    }
+
+    @Test
+    public void testEquality() {
+        DefaultVirtualDevice device1 =
+                new DefaultVirtualDevice(NetworkId.networkId(0), DeviceId.deviceId(deviceIdValue1));
+        DefaultVirtualDevice device2 =
+                new DefaultVirtualDevice(NetworkId.networkId(0), DeviceId.deviceId(deviceIdValue2));
+        ConnectPoint src = new ConnectPoint(device1.id(), PortNumber.portNumber(1));
+        ConnectPoint dst = new ConnectPoint(device2.id(), PortNumber.portNumber(2));
+
+        DefaultVirtualLink link1 = new DefaultVirtualLink(NetworkId.networkId(0), src, dst, TunnelId.valueOf(0));
+        DefaultVirtualLink link2 = new DefaultVirtualLink(NetworkId.networkId(0), src, dst, TunnelId.valueOf(0));
+        DefaultVirtualLink link3 = new DefaultVirtualLink(NetworkId.networkId(0), src, dst, TunnelId.valueOf(1));
+        DefaultVirtualLink link4 = new DefaultVirtualLink(NetworkId.networkId(1), src, dst, TunnelId.valueOf(0));
+
+        new EqualsTester().addEqualityGroup(link1, link2).addEqualityGroup(link3)
+                .addEqualityGroup(link4).testEquals();
+    }
+}
diff --git a/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.java b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.java
new file mode 100644
index 0000000..c8f79b1
--- /dev/null
+++ b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetworkTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+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/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java
new file mode 100644
index 0000000..6838ef5
--- /dev/null
+++ b/incubator/api/src/test/java/org/onosproject/incubator/net/virtual/DefaultVirtualPortTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Test of the default virtual port model entity.
+ */
+public class DefaultVirtualPortTest {
+    final String deviceIdValue1 = "DEVICE_ID1";
+    final String deviceIdValue2 = "DEVICE_ID2";
+
+    /**
+     * 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), DeviceId.deviceId(deviceIdValue1));
+        DefaultVirtualDevice device2 =
+                new DefaultVirtualDevice(NetworkId.networkId(0), DeviceId.deviceId(deviceIdValue2));
+
+        Port portA = new DefaultPort(device1, PortNumber.portNumber(1), true);
+        Port portB = new DefaultPort(device1, PortNumber.portNumber(2), true);
+        Port portC = new DefaultPort(device2, PortNumber.portNumber(2), true);
+
+        DefaultVirtualPort port1 =
+                new DefaultVirtualPort(NetworkId.networkId(0), device1, PortNumber.portNumber(1), portA);
+        DefaultVirtualPort port2 =
+                new DefaultVirtualPort(NetworkId.networkId(0), device1, PortNumber.portNumber(1), portA);
+        DefaultVirtualPort port3 =
+                new DefaultVirtualPort(NetworkId.networkId(0), device1, PortNumber.portNumber(2), portB);
+        DefaultVirtualPort port4 =
+                new DefaultVirtualPort(NetworkId.networkId(1), device2, PortNumber.portNumber(2), portC);
+
+
+        new EqualsTester().addEqualityGroup(port1, port2).addEqualityGroup(port3)
+                .addEqualityGroup(port4).testEquals();
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
index fe9f884..f7c54d6 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2016 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@
 @Service
 public class VirtualNetworkManager
         extends AbstractListenerProviderRegistry<VirtualNetworkEvent, VirtualNetworkListener,
-                                                 VirtualNetworkProvider, VirtualNetworkProviderService>
+        VirtualNetworkProvider, VirtualNetworkProviderService>
         implements VirtualNetworkService, VirtualNetworkAdminService, VirtualNetworkProviderRegistry {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
@@ -70,19 +70,23 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected VirtualNetworkStore store;
 
-    private VirtualNetworkStoreDelegate delegate = new InternalStoreDelegate();
+    private VirtualNetworkStoreDelegate delegate = this::post;
 
     // TODO: figure out how to coordinate "implementation" of a virtual network in a cluster
 
     @Activate
     protected void activate() {
         store.setDelegate(delegate);
+        eventDispatcher.addSink(VirtualNetworkEvent.class, listenerRegistry);
+
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
         store.unsetDelegate(delegate);
+        eventDispatcher.removeSink(VirtualNetworkEvent.class);
+
         log.info("Stopped");
     }
 
@@ -211,13 +215,4 @@
         }
     }
 
-    // Auxiliary store delegate to receive notification about changes in
-    // the virtual network configuration store state - by the store itself.
-    private class InternalStoreDelegate implements VirtualNetworkStoreDelegate {
-        @Override
-        public void notify(VirtualNetworkEvent event) {
-            post(event);
-        }
-    }
-
 }
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
new file mode 100644
index 0000000..8976a7e
--- /dev/null
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.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.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.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.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.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.store.virtual.impl.DistributedVirtualNetworkStore;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetTestTools;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.*;
+
+/**
+ * Junit tests for VirtualNetworkManager.
+ */
+public class VirtualNetworkManagerTest {
+    final String tenantIdValue1 = "TENANT_ID1";
+    final String tenantIdValue2 = "TENANT_ID2";
+    final String deviceIdValue1 = "DEVICE_ID1";
+    final String deviceIdValue2 = "DEVICE_ID2";
+
+    private VirtualNetworkManager manager;
+    private VirtualNetworkService virtualNetworkManagerService;
+    private DistributedVirtualNetworkStore virtualNetworkManagerStore;
+    private CoreService coreService;
+    protected TestListener listener = new TestListener();
+
+    @Before
+    public void setUp() throws Exception {
+        virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+
+        coreService = new TestCoreService();
+        virtualNetworkManagerStore.setCoreService(coreService);
+        TestUtils.setField(coreService, "coreService", new TestCoreService());
+        TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
+        virtualNetworkManagerStore.activate();
+
+        manager = new VirtualNetworkManager();
+        manager.store = virtualNetworkManagerStore;
+        manager.addListener(listener);
+        NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
+        manager.activate();
+        virtualNetworkManagerService = manager;
+
+    }
+
+    @After
+    public void tearDown() {
+        virtualNetworkManagerStore.deactivate();
+        manager.removeListener(listener);
+        manager.deactivate();
+        NetTestTools.injectEventDispatcher(manager, null);
+    }
+
+    /**
+     * 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_UNREGISTERED, VirtualNetworkEvent.Type.TENANT_REGISTERED,
+                       VirtualNetworkEvent.Type.TENANT_REGISTERED, VirtualNetworkEvent.Type.TENANT_UNREGISTERED,
+                       VirtualNetworkEvent.Type.TENANT_UNREGISTERED);
+    }
+
+    /**
+     * Tests adding a null virtual network.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testCreateNullVirtualNetwork() {
+        manager.createVirtualNetwork(null);
+    }
+
+    /**
+     * 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());
+
+        for (VirtualNetwork virtualNetwork : virtualNetworks) {
+            manager.removeVirtualNetwork(virtualNetwork.id());
+            // attempt to remove the same virtual network again.
+            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);
+    }
+
+    /**
+     * 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(), DeviceId.deviceId(deviceIdValue1));
+    }
+
+    /**
+     * Tests add and remove of virtual devices.
+     */
+    @Test
+    public void testAddRemoveVirtualDevice() {
+        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        VirtualNetwork virtualNetwork1 = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+        VirtualNetwork virtualNetwork2 = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+        manager.createVirtualDevice(virtualNetwork1.id(), DeviceId.deviceId(deviceIdValue1));
+        manager.createVirtualDevice(virtualNetwork2.id(), DeviceId.deviceId(deviceIdValue2));
+
+        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());
+            // attempt to remove the same virtual device again.
+            manager.removeVirtualDevice(virtualNetwork1.id(), virtualDevice.id());
+        }
+        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(VirtualNetworkEvent.Type.TENANT_REGISTERED, VirtualNetworkEvent.Type.NETWORK_ADDED,
+                       VirtualNetworkEvent.Type.NETWORK_ADDED);
+    }
+
+    /**
+     * 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(), DeviceId.deviceId(deviceIdValue1));
+        VirtualDevice dstVirtualDevice =
+                manager.createVirtualDevice(virtualNetwork1.id(), DeviceId.deviceId(deviceIdValue2));
+        ConnectPoint src = new ConnectPoint(srcVirtualDevice.id(), PortNumber.portNumber(1));
+        ConnectPoint dst = new ConnectPoint(dstVirtualDevice.id(), PortNumber.portNumber(2));
+        manager.createVirtualLink(virtualNetwork1.id(), src, dst, TunnelId.valueOf(0));
+        manager.createVirtualLink(virtualNetwork1.id(), dst, src, TunnelId.valueOf(1));
+
+        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());
+    }
+
+    /**
+     * Tests add and remove of virtual ports.
+     */
+    @Test
+    public void testAddRemoveVirtualPort() {
+        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
+        VirtualNetwork virtualNetwork1 = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
+        VirtualDevice srcVirtualDevice =
+                manager.createVirtualDevice(virtualNetwork1.id(), DeviceId.deviceId(deviceIdValue1));
+        VirtualDevice dstVirtualDevice =
+                manager.createVirtualDevice(virtualNetwork1.id(), DeviceId.deviceId(deviceIdValue2));
+        Port port = new DefaultPort(srcVirtualDevice, PortNumber.portNumber(1), true);
+
+        manager.createVirtualPort(virtualNetwork1.id(), srcVirtualDevice.id(), PortNumber.portNumber(1), port);
+        manager.createVirtualPort(virtualNetwork1.id(), dstVirtualDevice.id(), PortNumber.portNumber(1), port);
+
+        Set<VirtualPort> virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), srcVirtualDevice.id());
+        assertNotNull("The virtual port set should not be null", virtualPorts);
+        assertEquals("The virtual port set size did not match.", 2, virtualPorts.size());
+
+        for (VirtualPort virtualPort : virtualPorts) {
+            manager.removeVirtualPort(virtualNetwork1.id(),
+                                      (DeviceId) virtualPort.element().id(), virtualPort.number());
+            // attempt to remove the same virtual port again.
+            manager.removeVirtualPort(virtualNetwork1.id(),
+                                      (DeviceId) virtualPort.element().id(), virtualPort.number());
+        }
+        virtualPorts = manager.getVirtualPorts(virtualNetwork1.id(), srcVirtualDevice.id());
+        assertTrue("The virtual port set should be empty.", virtualPorts.isEmpty());
+    }
+
+    /**
+     * 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 {
+
+        protected List<VirtualNetworkEvent> events = Lists.newArrayList();
+
+        @Override
+        public void event(VirtualNetworkEvent event) {
+            events.add(event);
+        }
+
+    }
+
+    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/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 69e56c0..65cee11 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2016 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,35 +15,59 @@
  */
 package org.onosproject.incubator.store.virtual.impl;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+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.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.incubator.net.virtual.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.VirtualNetworkEvent;
+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.Port;
 import org.onosproject.net.PortNumber;
 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.slf4j.Logger;
 
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
+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 network store.
+ * Implementation of the virtual network store.
  */
 @Component(immediate = true)
 @Service
@@ -53,95 +77,404 @@
 
     private final Logger log = getLogger(getClass());
 
-    // TODO: track tenants by ID
-    // TODO: track networks by ID and by tenants
-    // TODO: track devices by network ID and device ID
-    // TODO: track devices by network ID
-    // TODO: setup block allocator for network IDs
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
 
-    // TODO: notify delegate
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    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> virtualMapListener = new InternalMapListener();
+
+    // 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<DeviceId, VirtualDevice> deviceIdVirtualDeviceConsistentMap;
+    private Map<DeviceId, VirtualDevice> deviceIdVirtualDeviceMap;
+
+    // Track device IDs by network Id
+    private ConsistentMap<NetworkId, Set<DeviceId>> networkIdDeviceIdSetConsistentMap;
+    private Map<NetworkId, Set<DeviceId>> networkIdDeviceIdSetMap;
+
+    // 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;
+
+    private static final Serializer SERIALIZER = Serializer
+            .using(new KryoNamespace.Builder().register(KryoNamespaces.API)
+                           .register(TenantId.class)
+                           .register(NetworkId.class).register(DeviceId.class)
+                           .register(VirtualNetwork.class)
+                           .register(VirtualDevice.class)
+                           .register(VirtualLink.class)
+                           .register(VirtualPort.class)
+                           .register(DeviceId.class)
+                           .register(Device.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(virtualMapListener);
+        networkIdVirtualNetworkMap = networkIdVirtualNetworkConsistentMap.asJavaMap();
+
+        tenantIdNetworkIdSetConsistentMap = storageService.<TenantId, Set<NetworkId>>consistentMapBuilder()
+                .withSerializer(SERIALIZER)
+                .withName("onos-tenantId-networkIds")
+                .withRelaxedReadConsistency()
+                .build();
+        tenantIdNetworkIdSetMap = tenantIdNetworkIdSetConsistentMap.asJavaMap();
+
+        deviceIdVirtualDeviceConsistentMap = storageService.<DeviceId, VirtualDevice>consistentMapBuilder()
+                .withSerializer(SERIALIZER)
+                .withName("onos-deviceId-virtualdevice")
+                .withRelaxedReadConsistency()
+                .build();
+        deviceIdVirtualDeviceMap = deviceIdVirtualDeviceConsistentMap.asJavaMap();
+
+        networkIdDeviceIdSetConsistentMap = storageService.<NetworkId, Set<DeviceId>>consistentMapBuilder()
+                .withSerializer(SERIALIZER)
+                .withName("onos-networkId-deviceIds")
+                .withRelaxedReadConsistency()
+                .build();
+        networkIdDeviceIdSetMap = networkIdDeviceIdSetConsistentMap.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-virtualportss")
+                .withRelaxedReadConsistency()
+                .build();
+        networkIdVirtualPortSetMap = networkIdVirtualPortSetConsistentMap.asJavaMap();
+
         log.info("Started");
     }
 
+    /**
+     * Distributed network store service deactivate method.
+     */
     @Deactivate
     public void deactivate() {
+        tenantIdSet.removeListener(setListener);
+        networkIdVirtualNetworkConsistentMap.removeListener(virtualMapListener);
         log.info("Stopped");
     }
 
+    /**
+     * This method is used for Junit tests to set the CoreService instance, which
+     * is required to set the IdGenerator instance.
+     *
+     * @param coreService core service instance
+     */
+    public void setCoreService(CoreService coreService) {
+        this.coreService = coreService;
+    }
+
     @Override
     public void addTenantId(TenantId tenantId) {
+        tenantIdSet.add(tenantId);
     }
 
     @Override
     public void removeTenantId(TenantId tenantId) {
+        tenantIdSet.remove(tenantId);
     }
 
     @Override
     public Set<TenantId> getTenantIds() {
-        return null;
+        return ImmutableSet.copyOf(tenantIdSet);
     }
 
     @Override
     public VirtualNetwork addNetwork(TenantId tenantId) {
-        return new DefaultVirtualNetwork(genNetworkId(), 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> virtualNetworkSet = tenantIdNetworkIdSetMap.get(tenantId);
+        if (virtualNetworkSet == null) {
+            virtualNetworkSet = new HashSet<>();
+        }
+        virtualNetworkSet.add(virtualNetwork.id());
+        tenantIdNetworkIdSetMap.put(tenantId, virtualNetworkSet);
+        return virtualNetwork;
     }
 
+    /**
+     * Returns a new network identifier from a virtual network block of identifiers.
+     *
+     * @return NetworkId network identifier
+     */
     private NetworkId genNetworkId() {
-        return NetworkId.networkId(0); // TODO: use a block allocator
+        return NetworkId.networkId(idGenerator.getNewId());
     }
 
 
     @Override
     public void removeNetwork(NetworkId networkId) {
+        // Make sure that the virtual network exists before attempting to remove it.
+        if (networkExists(networkId)) {
+            VirtualNetwork virtualNetwork = networkIdVirtualNetworkMap.get(networkId);
+            if (virtualNetwork == null) {
+                return;
+            }
+            //TODO update both maps in one transaction.
+            TenantId tenantId = virtualNetwork.tenantId();
+            networkIdVirtualNetworkMap.compute(networkId, (id, existingVirtualNetwork) -> null);
+
+
+            Set<NetworkId> virtualNetworkSet = tenantIdNetworkIdSetMap.get(tenantId);
+            tenantIdNetworkIdSetMap.compute(virtualNetwork.tenantId(), (id, existingNetworkIds) -> {
+                if (existingNetworkIds == null || existingNetworkIds.isEmpty()) {
+                    return ImmutableSet.of();
+                } else {
+                    return ImmutableSet.<NetworkId>builder()
+                            .addAll(Sets.difference(existingNetworkIds,
+                                                    ImmutableSet.copyOf(virtualNetworkSet)))
+                            .build();
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns if the network identifier exists.
+     *
+     * @param networkId network identifier
+     * @return true if the network identifier exists, false otherwise.
+     */
+    private boolean networkExists(NetworkId networkId) {
+        return (networkIdVirtualNetworkMap.containsKey(networkId));
     }
 
     @Override
     public VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId) {
-        return new DefaultVirtualDevice(networkId, deviceId);
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<DeviceId> deviceIdSet = networkIdDeviceIdSetMap.get(networkId);
+        if (deviceIdSet == null) {
+            deviceIdSet = new HashSet<>();
+        }
+        VirtualDevice virtualDevice = new DefaultVirtualDevice(networkId, deviceId);
+        //TODO update both maps in one transaction.
+        deviceIdVirtualDeviceMap.put(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.");
+        //TODO update both maps in one transaction.
+        Set<DeviceId> deviceIdSet = networkIdDeviceIdSetMap.get(networkId);
+        if (deviceIdSet != null) {
+            networkIdDeviceIdSetMap.compute(networkId, (id, existingDeviceIds) -> {
+                if (existingDeviceIds == null || existingDeviceIds.isEmpty()) {
+                    return ImmutableSet.of();
+                } else {
+                    return ImmutableSet.<DeviceId>builder()
+                            .addAll(Sets.difference(existingDeviceIds,
+                                                    ImmutableSet.copyOf(deviceIdSet)))
+                            .build();
+                }
+            });
+
+            deviceIdVirtualDeviceMap.compute(deviceId, (id, existingVirtualDevice) -> null);
+
+            log.info("The deviceIdVirtualDeviceMap size is: " + getDevices(networkId));
+        }
     }
 
     @Override
     public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId realizedBy) {
-        return null;
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
+        if (virtualLinkSet == null) {
+            virtualLinkSet = new HashSet<>();
+        }
+        VirtualLink virtualLink = new DefaultVirtualLink(networkId, src, dst, realizedBy);
+        virtualLinkSet.add(virtualLink);
+        networkIdVirtualLinkSetMap.put(networkId, virtualLinkSet);
+        return virtualLink;
     }
 
     @Override
     public void removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(networkId);
+        if (virtualLinkSet != null) {
+            networkIdVirtualLinkSetMap.compute(networkId, (id, existingVirtualLinks) -> {
+                if (existingVirtualLinks == null || existingVirtualLinks.isEmpty()) {
+                    return ImmutableSet.of();
+                } else {
+                    return ImmutableSet.<VirtualLink>builder()
+                            .addAll(Sets.difference(existingVirtualLinks,
+                                                    ImmutableSet.copyOf(virtualLinkSet)))
+                            .build();
+                }
+            });
+        }
     }
 
     @Override
     public VirtualPort addPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber, Port realizedBy) {
-        return null;
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+        if (virtualPortSet == null) {
+            virtualPortSet = new HashSet<>();
+        }
+        Device device = deviceIdVirtualDeviceMap.get(deviceId);
+        checkNotNull(device, "The device has not been created for deviceId: " + deviceId);
+        VirtualPort virtualPort = new DefaultVirtualPort(networkId, device, portNumber, realizedBy);
+        virtualPortSet.add(virtualPort);
+        networkIdVirtualPortSetMap.put(networkId, virtualPortSet);
+        return virtualPort;
     }
 
     @Override
     public void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualPort> virtualPortSet = networkIdVirtualPortSetMap.get(networkId);
+        if (virtualPortSet != null) {
+            networkIdVirtualPortSetMap.compute(networkId, (id, existingVirtualPorts) -> {
+                if (existingVirtualPorts == null || existingVirtualPorts.isEmpty()) {
+                    return ImmutableSet.of();
+                } else {
+                    return ImmutableSet.<VirtualPort>builder()
+                            .addAll(Sets.difference(existingVirtualPorts,
+                                                    ImmutableSet.copyOf(virtualPortSet)))
+                            .build();
+                }
+            });
+        }
     }
 
     @Override
     public Set<VirtualNetwork> getNetworks(TenantId tenantId) {
-        return null;
+        Set<NetworkId> networkIdSet = tenantIdNetworkIdSetMap.get(tenantId);
+        Set<VirtualNetwork> virtualNetworkSet = new HashSet<>();
+        if (networkIdSet != null) {
+            networkIdSet.forEach(networkId -> virtualNetworkSet.add(networkIdVirtualNetworkMap.get(networkId)));
+        }
+        return ImmutableSet.copyOf(virtualNetworkSet);
     }
 
     @Override
     public Set<VirtualDevice> getDevices(NetworkId networkId) {
-        return null;
+        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(deviceId)));
+        }
+        return ImmutableSet.copyOf(virtualDeviceSet);
     }
 
     @Override
     public Set<VirtualLink> getLinks(NetworkId networkId) {
-        return null;
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualLink> virtualLinkSet = new HashSet<>();
+        virtualLinkSet.addAll(networkIdVirtualLinkSetMap.get(networkId));
+        return ImmutableSet.copyOf(virtualLinkSet);
     }
 
     @Override
     public Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId) {
-        return null;
+        checkState(networkExists(networkId), "The network has not been added.");
+        Set<VirtualPort> virtualPortSet = new HashSet<>();
+        virtualPortSet.addAll(networkIdVirtualPortSetMap.get(networkId));
+        return ImmutableSet.copyOf(virtualPortSet);
+    }
+
+    /**
+     * 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 implements MapEventListener<NetworkId, VirtualNetwork> {
+        @Override
+        public void event(MapEvent<NetworkId, VirtualNetwork> event) {
+            NetworkId networkId = checkNotNull(event.key());
+            VirtualNetworkEvent.Type type = null;
+            switch (event.type()) {
+                case INSERT:
+                    type = VirtualNetworkEvent.Type.NETWORK_ADDED;
+                    break;
+                case UPDATE:
+                    if ((event.oldValue().value() != null) && (event.newValue().value() == null)) {
+                        type = VirtualNetworkEvent.Type.NETWORK_REMOVED;
+                    } else {
+                        type = VirtualNetworkEvent.Type.NETWORK_UPDATED;
+                    }
+                    break;
+                case REMOVE:
+                    type = VirtualNetworkEvent.Type.NETWORK_REMOVED;
+                    break;
+                default:
+                    log.error("Unsupported event type: " + event.type());
+            }
+            notifyDelegate(new VirtualNetworkEvent(type, networkId));
+        }
     }
 }