[ONOS-6371] (vNet) mastership manager implementation

Implment a mastership manager for mastership service, mastership
admin service, and mastership term service for virtual network.
Moreover, a simple balancing roles method is included that balancing
accoridng to a virtual network identifier.

Change-Id: I68064775734438d59c576106c49d1ea72836caa8
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java
index 273ecbb..b651fb1 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMastershipManager.java
@@ -16,11 +16,16 @@
 
 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;
@@ -33,18 +38,39 @@
 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 static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.newArrayList;
+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.
      *
@@ -54,59 +80,117 @@
     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 MetricsService metricsService() {
-        return null;
+    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 Set<DeviceId> getDevicesOf(NodeId nodeId) {
+        checkNotNull(nodeId, NODE_ID_NULL);
+
+        return store.getDevices(networkId, nodeId);
     }
 
     @Override
     public MastershipTerm getMastershipTerm(DeviceId deviceId) {
-        return null;
+        return store.getTermFor(networkId, deviceId);
     }
 
     @Override
-    public CompletableFuture<Void> setRole(NodeId instance, DeviceId deviceId, MastershipRole role) {
+    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 = newArrayList(clusterService.getNodes());
+        nodes.sort(Comparator.comparing(ControllerNode::id));
 
-    }
+        //Pick a node using network Id,
+        NodeId masterNode = nodes.get((int) (networkId.id() % nodes.size())).id();
 
-    @Override
-    public MastershipRole getLocalRole(DeviceId deviceId) {
-        return null;
-    }
+        List<CompletableFuture<Void>> setRoleFutures = Lists.newLinkedList();
+        for (VirtualDevice device : manager.getVirtualDevices(networkId)) {
+            setRoleFutures.add(setRole(masterNode, device.id(), MastershipRole.MASTER));
+        }
 
-    @Override
-    public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
-        return null;
-    }
+        CompletableFuture<Void> balanceRolesFuture = CompletableFuture.allOf(
+                setRoleFutures.toArray(new CompletableFuture[setRoleFutures.size()]));
 
-    @Override
-    public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
-        return null;
-    }
-
-    @Override
-    public NodeId getMasterFor(DeviceId deviceId) {
-        return null;
-    }
-
-    @Override
-    public RoleInfo getNodesFor(DeviceId deviceId) {
-        return null;
-    }
-
-    @Override
-    public Set<DeviceId> getDevicesOf(NodeId nodeId) {
-        return null;
+        Futures.getUnchecked(balanceRolesFuture);
     }
 
     public class InternalDelegate implements MastershipStoreDelegate {
@@ -115,4 +199,5 @@
             post(event);
         }
     }
+
 }