Madan Jampani
Committed by Gerrit Code Review

ONOS-1883: Fix for lost flow rules on CLI directed mastership changes.

- Made all mastership role change operations asynchronous, which they are.
- In flowrule store we now check to see if any new backups need to be made when a device backup location (standby) changes
- In device mastership store we now wait briefly before we step down from mastership after promoting a new candidate as highest standy

Change-Id: Icb76cf4d0d23403053a3fd5a458a940b847da49f
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onosproject.mastership; 16 package org.onosproject.mastership;
17 17
18 import java.util.Set; 18 import java.util.Set;
19 +import java.util.concurrent.CompletableFuture;
19 20
20 import org.onosproject.cluster.NodeId; 21 import org.onosproject.cluster.NodeId;
21 import org.onosproject.cluster.RoleInfo; 22 import org.onosproject.cluster.RoleInfo;
...@@ -80,7 +81,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -80,7 +81,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
80 * @param deviceId device identifier 81 * @param deviceId device identifier
81 * @return a mastership event 82 * @return a mastership event
82 */ 83 */
83 - MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId); 84 + CompletableFuture<MastershipEvent> setMaster(NodeId nodeId, DeviceId deviceId);
84 85
85 /** 86 /**
86 * Returns the current master and number of past mastership hand-offs 87 * Returns the current master and number of past mastership hand-offs
...@@ -100,7 +101,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -100,7 +101,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
100 * @param deviceId device to revoke mastership role for 101 * @param deviceId device to revoke mastership role for
101 * @return a mastership event 102 * @return a mastership event
102 */ 103 */
103 - MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId); 104 + CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId);
104 105
105 /** 106 /**
106 * Allows a controller instance to give up its current role for a device. 107 * Allows a controller instance to give up its current role for a device.
...@@ -111,7 +112,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -111,7 +112,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
111 * @param deviceId device to revoke mastership role for 112 * @param deviceId device to revoke mastership role for
112 * @return a mastership event 113 * @return a mastership event
113 */ 114 */
114 - MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId); 115 + CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId);
115 116
116 /** 117 /**
117 * Removes all the roles for the specified controller instance. 118 * Removes all the roles for the specified controller instance.
......
...@@ -17,6 +17,7 @@ package org.onosproject.cluster.impl; ...@@ -17,6 +17,7 @@ package org.onosproject.cluster.impl;
17 17
18 import com.codahale.metrics.Timer; 18 import com.codahale.metrics.Timer;
19 import com.codahale.metrics.Timer.Context; 19 import com.codahale.metrics.Timer.Context;
20 +
20 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate; 23 import org.apache.felix.scr.annotations.Deactivate;
...@@ -50,6 +51,8 @@ import java.util.Iterator; ...@@ -50,6 +51,8 @@ import java.util.Iterator;
50 import java.util.List; 51 import java.util.List;
51 import java.util.Map; 52 import java.util.Map;
52 import java.util.Set; 53 import java.util.Set;
54 +import java.util.concurrent.CompletableFuture;
55 +
53 import static com.google.common.base.Preconditions.checkNotNull; 56 import static com.google.common.base.Preconditions.checkNotNull;
54 import static com.google.common.collect.Lists.newArrayList; 57 import static com.google.common.collect.Lists.newArrayList;
55 import static org.onlab.metrics.MetricsUtil.startTimer; 58 import static org.onlab.metrics.MetricsUtil.startTimer;
...@@ -111,26 +114,28 @@ public class MastershipManager ...@@ -111,26 +114,28 @@ public class MastershipManager
111 checkNotNull(deviceId, DEVICE_ID_NULL); 114 checkNotNull(deviceId, DEVICE_ID_NULL);
112 checkNotNull(role, ROLE_NULL); 115 checkNotNull(role, ROLE_NULL);
113 116
114 - MastershipEvent event = null; 117 + CompletableFuture<MastershipEvent> eventFuture = null;
115 118
116 switch (role) { 119 switch (role) {
117 case MASTER: 120 case MASTER:
118 - event = store.setMaster(nodeId, deviceId); 121 + eventFuture = store.setMaster(nodeId, deviceId);
119 break; 122 break;
120 case STANDBY: 123 case STANDBY:
121 - event = store.setStandby(nodeId, deviceId); 124 + eventFuture = store.setStandby(nodeId, deviceId);
122 break; 125 break;
123 case NONE: 126 case NONE:
124 - event = store.relinquishRole(nodeId, deviceId); 127 + eventFuture = store.relinquishRole(nodeId, deviceId);
125 break; 128 break;
126 default: 129 default:
127 log.info("Unknown role; ignoring"); 130 log.info("Unknown role; ignoring");
128 return; 131 return;
129 } 132 }
130 133
131 - if (event != null) { 134 + eventFuture.whenComplete((event, error) -> {
132 - post(event); 135 + if (event != null) {
133 - } 136 + post(event);
137 + }
138 + });
134 } 139 }
135 140
136 @Override 141 @Override
...@@ -141,12 +146,12 @@ public class MastershipManager ...@@ -141,12 +146,12 @@ public class MastershipManager
141 146
142 @Override 147 @Override
143 public void relinquishMastership(DeviceId deviceId) { 148 public void relinquishMastership(DeviceId deviceId) {
144 - MastershipEvent event = null; 149 + store.relinquishRole(clusterService.getLocalNode().id(), deviceId)
145 - event = store.relinquishRole( 150 + .whenComplete((event, error) -> {
146 - clusterService.getLocalNode().id(), deviceId); 151 + if (event != null) {
147 - if (event != null) { 152 + post(event);
148 - post(event); 153 + }
149 - } 154 + });
150 } 155 }
151 156
152 @Override 157 @Override
......
...@@ -62,6 +62,8 @@ import org.onosproject.store.cluster.messaging.ClusterCommunicationService; ...@@ -62,6 +62,8 @@ import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
62 import org.onosproject.store.cluster.messaging.ClusterMessage; 62 import org.onosproject.store.cluster.messaging.ClusterMessage;
63 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 63 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
64 import org.onosproject.store.flow.ReplicaInfo; 64 import org.onosproject.store.flow.ReplicaInfo;
65 +import org.onosproject.store.flow.ReplicaInfoEvent;
66 +import org.onosproject.store.flow.ReplicaInfoEventListener;
65 import org.onosproject.store.flow.ReplicaInfoService; 67 import org.onosproject.store.flow.ReplicaInfoService;
66 import org.onosproject.store.serializers.KryoSerializer; 68 import org.onosproject.store.serializers.KryoSerializer;
67 import org.onosproject.store.serializers.StoreSerializer; 69 import org.onosproject.store.serializers.StoreSerializer;
...@@ -181,6 +183,7 @@ public class NewDistributedFlowRuleStore ...@@ -181,6 +183,7 @@ public class NewDistributedFlowRuleStore
181 registerMessageHandlers(messageHandlingExecutor); 183 registerMessageHandlers(messageHandlingExecutor);
182 184
183 if (backupEnabled) { 185 if (backupEnabled) {
186 + replicaInfoManager.addListener(flowTable);
184 backupTask = backupSenderExecutor.scheduleWithFixedDelay( 187 backupTask = backupSenderExecutor.scheduleWithFixedDelay(
185 flowTable::backup, 188 flowTable::backup,
186 0, 189 0,
...@@ -193,6 +196,10 @@ public class NewDistributedFlowRuleStore ...@@ -193,6 +196,10 @@ public class NewDistributedFlowRuleStore
193 196
194 @Deactivate 197 @Deactivate
195 public void deactivate(ComponentContext context) { 198 public void deactivate(ComponentContext context) {
199 + if (backupEnabled) {
200 + replicaInfoManager.removeListener(flowTable);
201 + backupTask.cancel(true);
202 + }
196 configService.unregisterProperties(getClass(), false); 203 configService.unregisterProperties(getClass(), false);
197 unregisterMessageHandlers(); 204 unregisterMessageHandlers();
198 messageHandlingExecutor.shutdownNow(); 205 messageHandlingExecutor.shutdownNow();
...@@ -232,9 +239,14 @@ public class NewDistributedFlowRuleStore ...@@ -232,9 +239,14 @@ public class NewDistributedFlowRuleStore
232 boolean restartBackupTask = false; 239 boolean restartBackupTask = false;
233 if (newBackupEnabled != backupEnabled) { 240 if (newBackupEnabled != backupEnabled) {
234 backupEnabled = newBackupEnabled; 241 backupEnabled = newBackupEnabled;
235 - if (!backupEnabled && backupTask != null) { 242 + if (!backupEnabled) {
236 - backupTask.cancel(false); 243 + replicaInfoManager.removeListener(flowTable);
237 - backupTask = null; 244 + if (backupTask != null) {
245 + backupTask.cancel(false);
246 + backupTask = null;
247 + }
248 + } else {
249 + replicaInfoManager.addListener(flowTable);
238 } 250 }
239 restartBackupTask = backupEnabled; 251 restartBackupTask = backupEnabled;
240 } 252 }
...@@ -590,7 +602,7 @@ public class NewDistributedFlowRuleStore ...@@ -590,7 +602,7 @@ public class NewDistributedFlowRuleStore
590 } 602 }
591 } 603 }
592 604
593 - private class InternalFlowTable { 605 + private class InternalFlowTable implements ReplicaInfoEventListener {
594 606
595 private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, Set<StoredFlowEntry>>> 607 private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, Set<StoredFlowEntry>>>
596 flowEntries = new ConcurrentHashMap<>(); 608 flowEntries = new ConcurrentHashMap<>();
...@@ -603,6 +615,43 @@ public class NewDistributedFlowRuleStore ...@@ -603,6 +615,43 @@ public class NewDistributedFlowRuleStore
603 return NewConcurrentHashMap.<FlowId, Set<StoredFlowEntry>>ifNeeded(); 615 return NewConcurrentHashMap.<FlowId, Set<StoredFlowEntry>>ifNeeded();
604 } 616 }
605 617
618 + @Override
619 + public void event(ReplicaInfoEvent event) {
620 + if (event.type() == ReplicaInfoEvent.Type.BACKUPS_CHANGED) {
621 + DeviceId deviceId = event.subject();
622 + if (!Objects.equal(local, replicaInfoManager.getReplicaInfoFor(deviceId).master())) {
623 + // ignore since this event is for a device this node does not manage.
624 + return;
625 + }
626 + NodeId latestBackupNode = getBackupNode(deviceId);
627 + NodeId existingBackupNode = lastBackupNodes.get(deviceId);
628 + if (Objects.equal(latestBackupNode, existingBackupNode)) {
629 + // ignore since backup location hasn't changed.
630 + return;
631 + }
632 + backupFlowEntries(latestBackupNode, Sets.newHashSet(deviceId));
633 + }
634 + }
635 +
636 + private void backupFlowEntries(NodeId nodeId, Set<DeviceId> deviceIds) {
637 + log.debug("Sending flowEntries for devices {} to {} as backup.", deviceIds, nodeId);
638 + Map<DeviceId, ConcurrentMap<FlowId, Set<StoredFlowEntry>>> deviceFlowEntries =
639 + Maps.newConcurrentMap();
640 + flowEntries.forEach((key, value) -> {
641 + if (deviceIds.contains(key)) {
642 + deviceFlowEntries.put(key, value);
643 + }
644 + });
645 + clusterCommunicator.unicast(deviceFlowEntries,
646 + FLOW_TABLE_BACKUP,
647 + SERIALIZER::encode,
648 + nodeId);
649 + deviceIds.forEach(id -> {
650 + lastBackupTimes.put(id, System.currentTimeMillis());
651 + lastBackupNodes.put(id, nodeId);
652 + });
653 + }
654 +
606 /** 655 /**
607 * Returns the flow table for specified device. 656 * Returns the flow table for specified device.
608 * 657 *
...@@ -662,7 +711,6 @@ public class NewDistributedFlowRuleStore ...@@ -662,7 +711,6 @@ public class NewDistributedFlowRuleStore
662 if (!backupEnabled) { 711 if (!backupEnabled) {
663 return; 712 return;
664 } 713 }
665 - //TODO: Force backup when backups change.
666 try { 714 try {
667 // determine the set of devices that we need to backup during this run. 715 // determine the set of devices that we need to backup during this run.
668 Set<DeviceId> devicesToBackup = mastershipService.getDevicesOf(local) 716 Set<DeviceId> devicesToBackup = mastershipService.getDevicesOf(local)
...@@ -686,35 +734,15 @@ public class NewDistributedFlowRuleStore ...@@ -686,35 +734,15 @@ public class NewDistributedFlowRuleStore
686 .add(deviceId); 734 .add(deviceId);
687 } 735 }
688 }); 736 });
689 -
690 // send the device flow entries to their respective backup nodes 737 // send the device flow entries to their respective backup nodes
691 - devicesToBackupByNode.forEach((nodeId, deviceIds) -> { 738 + devicesToBackupByNode.forEach(this::backupFlowEntries);
692 - Map<DeviceId, ConcurrentMap<FlowId, Set<StoredFlowEntry>>> deviceFlowEntries =
693 - Maps.newConcurrentMap();
694 - flowEntries.forEach((key, value) -> {
695 - if (deviceIds.contains(key)) {
696 - deviceFlowEntries.put(key, value);
697 - }
698 - });
699 - clusterCommunicator.unicast(deviceFlowEntries,
700 - FLOW_TABLE_BACKUP,
701 - SERIALIZER::encode,
702 - nodeId);
703 - });
704 -
705 - // update state for use in subsequent run.
706 - devicesToBackupByNode.forEach((node, devices) -> {
707 - devices.forEach(id -> {
708 - lastBackupTimes.put(id, System.currentTimeMillis());
709 - lastBackupNodes.put(id, node);
710 - });
711 - });
712 } catch (Exception e) { 739 } catch (Exception e) {
713 log.error("Backup failed.", e); 740 log.error("Backup failed.", e);
714 } 741 }
715 } 742 }
716 743
717 private void onBackupReceipt(Map<DeviceId, Map<FlowId, Set<StoredFlowEntry>>> flowTables) { 744 private void onBackupReceipt(Map<DeviceId, Map<FlowId, Set<StoredFlowEntry>>> flowTables) {
745 + log.debug("Received flows for {} to backup", flowTables.keySet());
718 Set<DeviceId> managedDevices = mastershipService.getDevicesOf(local); 746 Set<DeviceId> managedDevices = mastershipService.getDevicesOf(local);
719 // Only process those devices are that not managed by the local node. 747 // Only process those devices are that not managed by the local node.
720 Maps.filterKeys(flowTables, deviceId -> !managedDevices.contains(deviceId)) 748 Maps.filterKeys(flowTables, deviceId -> !managedDevices.contains(deviceId))
......
...@@ -25,8 +25,11 @@ import static com.google.common.base.Preconditions.checkArgument; ...@@ -25,8 +25,11 @@ import static com.google.common.base.Preconditions.checkArgument;
25 import java.util.List; 25 import java.util.List;
26 import java.util.Map; 26 import java.util.Map;
27 import java.util.Set; 27 import java.util.Set;
28 +import java.util.concurrent.CompletableFuture;
28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors; 30 import java.util.concurrent.Executors;
31 +import java.util.concurrent.ScheduledExecutorService;
32 +import java.util.concurrent.TimeUnit;
30 import java.util.regex.Matcher; 33 import java.util.regex.Matcher;
31 import java.util.regex.Pattern; 34 import java.util.regex.Pattern;
32 import java.util.stream.Collectors; 35 import java.util.stream.Collectors;
...@@ -98,11 +101,13 @@ public class ConsistentDeviceMastershipStore ...@@ -98,11 +101,13 @@ public class ConsistentDeviceMastershipStore
98 Pattern.compile("device:(.*)"); 101 Pattern.compile("device:(.*)");
99 102
100 private ExecutorService messageHandlingExecutor; 103 private ExecutorService messageHandlingExecutor;
104 + private ScheduledExecutorService transferExecutor;
101 private final LeadershipEventListener leadershipEventListener = 105 private final LeadershipEventListener leadershipEventListener =
102 new InternalDeviceMastershipEventListener(); 106 new InternalDeviceMastershipEventListener();
103 107
104 private static final String NODE_ID_NULL = "Node ID cannot be null"; 108 private static final String NODE_ID_NULL = "Node ID cannot be null";
105 - private static final String DEVICE_ID_NULL = "Device ID cannot be null";; 109 + private static final String DEVICE_ID_NULL = "Device ID cannot be null";
110 + private static final int WAIT_BEFORE_MASTERSHIP_HANDOFF_MILLIS = 3000;
106 111
107 public static final StoreSerializer SERIALIZER = new KryoSerializer() { 112 public static final StoreSerializer SERIALIZER = new KryoSerializer() {
108 @Override 113 @Override
...@@ -119,7 +124,11 @@ public class ConsistentDeviceMastershipStore ...@@ -119,7 +124,11 @@ public class ConsistentDeviceMastershipStore
119 @Activate 124 @Activate
120 public void activate() { 125 public void activate() {
121 messageHandlingExecutor = 126 messageHandlingExecutor =
122 - Executors.newSingleThreadExecutor(groupedThreads("onos/store/device/mastership", "message-handler")); 127 + Executors.newSingleThreadExecutor(
128 + groupedThreads("onos/store/device/mastership", "message-handler"));
129 + transferExecutor =
130 + Executors.newSingleThreadScheduledExecutor(
131 + groupedThreads("onos/store/device/mastership", "mastership-transfer-executor"));
123 clusterCommunicator.<DeviceId, MastershipRole>addSubscriber(ROLE_QUERY_SUBJECT, 132 clusterCommunicator.<DeviceId, MastershipRole>addSubscriber(ROLE_QUERY_SUBJECT,
124 SERIALIZER::decode, 133 SERIALIZER::decode,
125 deviceId -> getRole(localNodeId, deviceId), 134 deviceId -> getRole(localNodeId, deviceId),
...@@ -127,7 +136,7 @@ public class ConsistentDeviceMastershipStore ...@@ -127,7 +136,7 @@ public class ConsistentDeviceMastershipStore
127 messageHandlingExecutor); 136 messageHandlingExecutor);
128 clusterCommunicator.<DeviceId, MastershipEvent>addSubscriber(ROLE_RELINQUISH_SUBJECT, 137 clusterCommunicator.<DeviceId, MastershipEvent>addSubscriber(ROLE_RELINQUISH_SUBJECT,
129 SERIALIZER::decode, 138 SERIALIZER::decode,
130 - deviceId -> relinquishRole(localNodeId, deviceId), 139 + this::relinquishLocalRole,
131 SERIALIZER::encode, 140 SERIALIZER::encode,
132 messageHandlingExecutor); 141 messageHandlingExecutor);
133 clusterCommunicator.addSubscriber(TRANSITION_FROM_MASTER_TO_STANDBY_SUBJECT, 142 clusterCommunicator.addSubscriber(TRANSITION_FROM_MASTER_TO_STANDBY_SUBJECT,
...@@ -147,6 +156,7 @@ public class ConsistentDeviceMastershipStore ...@@ -147,6 +156,7 @@ public class ConsistentDeviceMastershipStore
147 clusterCommunicator.removeSubscriber(ROLE_RELINQUISH_SUBJECT); 156 clusterCommunicator.removeSubscriber(ROLE_RELINQUISH_SUBJECT);
148 clusterCommunicator.removeSubscriber(TRANSITION_FROM_MASTER_TO_STANDBY_SUBJECT); 157 clusterCommunicator.removeSubscriber(TRANSITION_FROM_MASTER_TO_STANDBY_SUBJECT);
149 messageHandlingExecutor.shutdown(); 158 messageHandlingExecutor.shutdown();
159 + transferExecutor.shutdown();
150 leadershipService.removeListener(leadershipEventListener); 160 leadershipService.removeListener(leadershipEventListener);
151 161
152 log.info("Stoppped."); 162 log.info("Stoppped.");
...@@ -246,26 +256,36 @@ public class ConsistentDeviceMastershipStore ...@@ -246,26 +256,36 @@ public class ConsistentDeviceMastershipStore
246 } 256 }
247 257
248 @Override 258 @Override
249 - public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 259 + public CompletableFuture<MastershipEvent> setMaster(NodeId nodeId, DeviceId deviceId) {
250 checkArgument(nodeId != null, NODE_ID_NULL); 260 checkArgument(nodeId != null, NODE_ID_NULL);
251 checkArgument(deviceId != null, DEVICE_ID_NULL); 261 checkArgument(deviceId != null, DEVICE_ID_NULL);
252 262
253 NodeId currentMaster = getMaster(deviceId); 263 NodeId currentMaster = getMaster(deviceId);
254 if (nodeId.equals(currentMaster)) { 264 if (nodeId.equals(currentMaster)) {
255 - return null; 265 + return CompletableFuture.completedFuture(null);
256 } else { 266 } else {
257 String leadershipTopic = createDeviceMastershipTopic(deviceId); 267 String leadershipTopic = createDeviceMastershipTopic(deviceId);
258 List<NodeId> candidates = leadershipService.getCandidates(leadershipTopic); 268 List<NodeId> candidates = leadershipService.getCandidates(leadershipTopic);
259 if (candidates.isEmpty()) { 269 if (candidates.isEmpty()) {
260 - return null; 270 + return CompletableFuture.completedFuture(null);
261 } 271 }
262 if (leadershipService.makeTopCandidate(leadershipTopic, nodeId)) { 272 if (leadershipService.makeTopCandidate(leadershipTopic, nodeId)) {
263 - return transitionFromMasterToStandby(deviceId); 273 + CompletableFuture<MastershipEvent> result = new CompletableFuture<>();
274 + // There is brief wait before we step down from mastership.
275 + // This is to ensure any work that happens when standby preference
276 + // order changes can complete. For example: flow entries need to be backed
277 + // to the new top standby (ONOS-1883)
278 + // FIXME: This potentially introduces a race-condition.
279 + // Right now role changes are only forced via CLI.
280 + transferExecutor.schedule(() -> {
281 + result.complete(transitionFromMasterToStandby(deviceId));
282 + }, WAIT_BEFORE_MASTERSHIP_HANDOFF_MILLIS, TimeUnit.MILLISECONDS);
283 + return result;
264 } else { 284 } else {
265 log.warn("Failed to promote {} to mastership for {}", nodeId, deviceId); 285 log.warn("Failed to promote {} to mastership for {}", nodeId, deviceId);
266 } 286 }
267 } 287 }
268 - return null; 288 + return CompletableFuture.completedFuture(null);
269 } 289 }
270 290
271 @Override 291 @Override
...@@ -278,13 +298,13 @@ public class ConsistentDeviceMastershipStore ...@@ -278,13 +298,13 @@ public class ConsistentDeviceMastershipStore
278 } 298 }
279 299
280 @Override 300 @Override
281 - public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) { 301 + public CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId) {
282 checkArgument(nodeId != null, NODE_ID_NULL); 302 checkArgument(nodeId != null, NODE_ID_NULL);
283 checkArgument(deviceId != null, DEVICE_ID_NULL); 303 checkArgument(deviceId != null, DEVICE_ID_NULL);
284 304
285 NodeId currentMaster = getMaster(deviceId); 305 NodeId currentMaster = getMaster(deviceId);
286 if (!nodeId.equals(currentMaster)) { 306 if (!nodeId.equals(currentMaster)) {
287 - return null; 307 + return CompletableFuture.completedFuture(null);
288 } 308 }
289 309
290 String leadershipTopic = createDeviceMastershipTopic(deviceId); 310 String leadershipTopic = createDeviceMastershipTopic(deviceId);
...@@ -304,20 +324,25 @@ public class ConsistentDeviceMastershipStore ...@@ -304,20 +324,25 @@ public class ConsistentDeviceMastershipStore
304 } 324 }
305 325
306 @Override 326 @Override
307 - public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) { 327 + public CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId) {
308 checkArgument(nodeId != null, NODE_ID_NULL); 328 checkArgument(nodeId != null, NODE_ID_NULL);
309 checkArgument(deviceId != null, DEVICE_ID_NULL); 329 checkArgument(deviceId != null, DEVICE_ID_NULL);
310 330
311 if (!nodeId.equals(localNodeId)) { 331 if (!nodeId.equals(localNodeId)) {
312 log.debug("Forwarding request to relinquish " 332 log.debug("Forwarding request to relinquish "
313 + "role for device {} to {}", deviceId, nodeId); 333 + "role for device {} to {}", deviceId, nodeId);
314 - return futureGetOrElse(clusterCommunicator.sendAndReceive( 334 + return clusterCommunicator.sendAndReceive(
315 deviceId, 335 deviceId,
316 ROLE_RELINQUISH_SUBJECT, 336 ROLE_RELINQUISH_SUBJECT,
317 SERIALIZER::encode, 337 SERIALIZER::encode,
318 SERIALIZER::decode, 338 SERIALIZER::decode,
319 - nodeId), null); 339 + nodeId);
320 } 340 }
341 + return CompletableFuture.completedFuture(relinquishLocalRole(deviceId));
342 + }
343 +
344 + private MastershipEvent relinquishLocalRole(DeviceId deviceId) {
345 + checkArgument(deviceId != null, DEVICE_ID_NULL);
321 346
322 // Check if this node is can be managed by this node. 347 // Check if this node is can be managed by this node.
323 if (!connectedDevices.contains(deviceId)) { 348 if (!connectedDevices.contains(deviceId)) {
......
...@@ -25,6 +25,7 @@ import java.util.List; ...@@ -25,6 +25,7 @@ import java.util.List;
25 import java.util.Map; 25 import java.util.Map;
26 import java.util.Map.Entry; 26 import java.util.Map.Entry;
27 import java.util.Set; 27 import java.util.Set;
28 +import java.util.concurrent.CompletableFuture;
28 29
29 import org.apache.felix.scr.annotations.Activate; 30 import org.apache.felix.scr.annotations.Activate;
30 import org.apache.felix.scr.annotations.Component; 31 import org.apache.felix.scr.annotations.Component;
...@@ -130,7 +131,7 @@ public class DistributedMastershipStore ...@@ -130,7 +131,7 @@ public class DistributedMastershipStore
130 } 131 }
131 132
132 @Override 133 @Override
133 - public MastershipEvent setMaster(NodeId newMaster, DeviceId deviceId) { 134 + public CompletableFuture<MastershipEvent> setMaster(NodeId newMaster, DeviceId deviceId) {
134 135
135 roleMap.lock(deviceId); 136 roleMap.lock(deviceId);
136 try { 137 try {
...@@ -147,7 +148,7 @@ public class DistributedMastershipStore ...@@ -147,7 +148,7 @@ public class DistributedMastershipStore
147 log.warn("{} was in both MASTER and STANDBY for {}", newMaster, deviceId); 148 log.warn("{} was in both MASTER and STANDBY for {}", newMaster, deviceId);
148 // trigger BACKUPS_CHANGED? 149 // trigger BACKUPS_CHANGED?
149 } 150 }
150 - return null; 151 + return CompletableFuture.completedFuture(null);
151 case STANDBY: 152 case STANDBY:
152 case NONE: 153 case NONE:
153 final NodeId currentMaster = rv.get(MASTER); 154 final NodeId currentMaster = rv.get(MASTER);
...@@ -163,10 +164,11 @@ public class DistributedMastershipStore ...@@ -163,10 +164,11 @@ public class DistributedMastershipStore
163 rv.reassign(newMaster, STANDBY, NONE); 164 rv.reassign(newMaster, STANDBY, NONE);
164 updateTerm(deviceId); 165 updateTerm(deviceId);
165 roleMap.put(deviceId, rv); 166 roleMap.put(deviceId, rv);
166 - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); 167 + return CompletableFuture.completedFuture(
168 + new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
167 default: 169 default:
168 log.warn("unknown Mastership Role {}", currentRole); 170 log.warn("unknown Mastership Role {}", currentRole);
169 - return null; 171 + return CompletableFuture.completedFuture(null);
170 } 172 }
171 } finally { 173 } finally {
172 roleMap.unlock(deviceId); 174 roleMap.unlock(deviceId);
...@@ -282,7 +284,7 @@ public class DistributedMastershipStore ...@@ -282,7 +284,7 @@ public class DistributedMastershipStore
282 } 284 }
283 285
284 @Override 286 @Override
285 - public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) { 287 + public CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId) {
286 // if nodeId was MASTER, rotate STANDBY 288 // if nodeId was MASTER, rotate STANDBY
287 // if nodeId was STANDBY no-op 289 // if nodeId was STANDBY no-op
288 // if nodeId was NONE, add to STANDBY 290 // if nodeId was NONE, add to STANDBY
...@@ -298,30 +300,33 @@ public class DistributedMastershipStore ...@@ -298,30 +300,33 @@ public class DistributedMastershipStore
298 updateTerm(deviceId); 300 updateTerm(deviceId);
299 if (newMaster != null) { 301 if (newMaster != null) {
300 roleMap.put(deviceId, rv); 302 roleMap.put(deviceId, rv);
301 - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); 303 + return CompletableFuture.completedFuture(
304 + new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
302 } else { 305 } else {
303 // no master candidate 306 // no master candidate
304 roleMap.put(deviceId, rv); 307 roleMap.put(deviceId, rv);
305 // TBD: Should there be new event type for no MASTER? 308 // TBD: Should there be new event type for no MASTER?
306 - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); 309 + return CompletableFuture.completedFuture(
310 + new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
307 } 311 }
308 case STANDBY: 312 case STANDBY:
309 - return null; 313 + return CompletableFuture.completedFuture(null);
310 case NONE: 314 case NONE:
311 rv.reassign(nodeId, NONE, STANDBY); 315 rv.reassign(nodeId, NONE, STANDBY);
312 roleMap.put(deviceId, rv); 316 roleMap.put(deviceId, rv);
313 - return new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()); 317 + return CompletableFuture.completedFuture(
318 + new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()));
314 default: 319 default:
315 log.warn("unknown Mastership Role {}", currentRole); 320 log.warn("unknown Mastership Role {}", currentRole);
316 } 321 }
317 - return null; 322 + return CompletableFuture.completedFuture(null);
318 } finally { 323 } finally {
319 roleMap.unlock(deviceId); 324 roleMap.unlock(deviceId);
320 } 325 }
321 } 326 }
322 327
323 @Override 328 @Override
324 - public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) { 329 + public CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId) {
325 // relinquishRole is basically set to None 330 // relinquishRole is basically set to None
326 331
327 // If nodeId was master reelect next and remove nodeId 332 // If nodeId was master reelect next and remove nodeId
...@@ -337,13 +342,14 @@ public class DistributedMastershipStore ...@@ -337,13 +342,14 @@ public class DistributedMastershipStore
337 if (newMaster != null) { 342 if (newMaster != null) {
338 updateTerm(deviceId); 343 updateTerm(deviceId);
339 roleMap.put(deviceId, rv); 344 roleMap.put(deviceId, rv);
340 - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); 345 + return CompletableFuture.completedFuture(
346 + new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
341 } else { 347 } else {
342 // No master candidate - no more backups, device is likely 348 // No master candidate - no more backups, device is likely
343 // fully disconnected 349 // fully disconnected
344 roleMap.put(deviceId, rv); 350 roleMap.put(deviceId, rv);
345 // Should there be new event type? 351 // Should there be new event type?
346 - return null; 352 + return CompletableFuture.completedFuture(null);
347 } 353 }
348 case STANDBY: 354 case STANDBY:
349 //fall through to reinforce relinquishment 355 //fall through to reinforce relinquishment
...@@ -351,13 +357,14 @@ public class DistributedMastershipStore ...@@ -351,13 +357,14 @@ public class DistributedMastershipStore
351 boolean modified = rv.reassign(nodeId, STANDBY, NONE); 357 boolean modified = rv.reassign(nodeId, STANDBY, NONE);
352 if (modified) { 358 if (modified) {
353 roleMap.put(deviceId, rv); 359 roleMap.put(deviceId, rv);
354 - return new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()); 360 + return CompletableFuture.completedFuture(
361 + new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()));
355 } 362 }
356 - return null; 363 + return CompletableFuture.completedFuture(null);
357 default: 364 default:
358 log.warn("unknown Mastership Role {}", currentRole); 365 log.warn("unknown Mastership Role {}", currentRole);
359 } 366 }
360 - return null; 367 + return CompletableFuture.completedFuture(null);
361 } finally { 368 } finally {
362 roleMap.unlock(deviceId); 369 roleMap.unlock(deviceId);
363 } 370 }
...@@ -374,10 +381,11 @@ public class DistributedMastershipStore ...@@ -374,10 +381,11 @@ public class DistributedMastershipStore
374 if (roleValue.contains(MASTER, nodeId) || 381 if (roleValue.contains(MASTER, nodeId) ||
375 roleValue.contains(STANDBY, nodeId)) { 382 roleValue.contains(STANDBY, nodeId)) {
376 383
377 - MastershipEvent event = relinquishRole(nodeId, deviceId); 384 + relinquishRole(nodeId, deviceId).whenComplete((event, error) -> {
378 - if (event != null) { 385 + if (event != null) {
379 - events.add(event); 386 + events.add(event);
380 - } 387 + }
388 + });
381 } 389 }
382 } 390 }
383 notifyDelegate(events); 391 notifyDelegate(events);
......
...@@ -44,6 +44,7 @@ import org.onosproject.store.hz.TestStoreManager; ...@@ -44,6 +44,7 @@ import org.onosproject.store.hz.TestStoreManager;
44 import org.onosproject.store.serializers.KryoSerializer; 44 import org.onosproject.store.serializers.KryoSerializer;
45 45
46 import com.google.common.collect.Sets; 46 import com.google.common.collect.Sets;
47 +import com.google.common.util.concurrent.Futures;
47 48
48 import static org.junit.Assert.assertEquals; 49 import static org.junit.Assert.assertEquals;
49 import static org.junit.Assert.assertNull; 50 import static org.junit.Assert.assertNull;
...@@ -168,15 +169,15 @@ public class DistributedMastershipStoreTest { ...@@ -168,15 +169,15 @@ public class DistributedMastershipStoreTest {
168 //populate maps with DID1, N1 as MASTER thru NONE case 169 //populate maps with DID1, N1 as MASTER thru NONE case
169 testStore.setCurrent(CN1); 170 testStore.setCurrent(CN1);
170 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1)); 171 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
171 - assertNull("wrong event:", dms.setMaster(N1, DID1)); 172 + assertNull("wrong event:", Futures.getUnchecked(dms.setMaster(N1, DID1)));
172 173
173 //switch over to N2 174 //switch over to N2
174 - assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type()); 175 + assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.setMaster(N2, DID1)).type());
175 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber()); 176 System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
176 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID1)); 177 assertEquals("wrong term", MastershipTerm.of(N2, 2), dms.getTermFor(DID1));
177 178
178 //orphan switch - should be rare case 179 //orphan switch - should be rare case
179 - assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type()); 180 + assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.setMaster(N2, DID2)).type());
180 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2)); 181 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
181 //disconnect and reconnect - sign of failing re-election or single-instance channel 182 //disconnect and reconnect - sign of failing re-election or single-instance channel
182 dms.roleMap.clear(); 183 dms.roleMap.clear();
...@@ -190,18 +191,18 @@ public class DistributedMastershipStoreTest { ...@@ -190,18 +191,18 @@ public class DistributedMastershipStoreTest {
190 testStore.setCurrent(CN1); 191 testStore.setCurrent(CN1);
191 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1)); 192 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
192 //no backup, no new MASTER/event 193 //no backup, no new MASTER/event
193 - assertNull("wrong event:", dms.relinquishRole(N1, DID1)); 194 + assertNull("wrong event:", Futures.getUnchecked(dms.relinquishRole(N1, DID1)));
194 195
195 dms.requestRole(DID1); 196 dms.requestRole(DID1);
196 197
197 //add backup CN2, get it elected MASTER by relinquishing 198 //add backup CN2, get it elected MASTER by relinquishing
198 testStore.setCurrent(CN2); 199 testStore.setCurrent(CN2);
199 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1)); 200 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
200 - assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type()); 201 + assertEquals("wrong event:", Type.MASTER_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID1)).type());
201 assertEquals("wrong master", N2, dms.getMaster(DID1)); 202 assertEquals("wrong master", N2, dms.getMaster(DID1));
202 203
203 //all nodes "give up" on device, which goes back to NONE. 204 //all nodes "give up" on device, which goes back to NONE.
204 - assertNull("wrong event:", dms.relinquishRole(N2, DID1)); 205 + assertNull("wrong event:", Futures.getUnchecked(dms.relinquishRole(N2, DID1)));
205 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1)); 206 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
206 207
207 assertEquals("wrong number of retired nodes", 2, 208 assertEquals("wrong number of retired nodes", 2,
...@@ -215,11 +216,11 @@ public class DistributedMastershipStoreTest { ...@@ -215,11 +216,11 @@ public class DistributedMastershipStoreTest {
215 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size()); 216 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
216 217
217 //If STANDBY, should drop to NONE 218 //If STANDBY, should drop to NONE
218 - assertEquals("wrong event:", Type.BACKUPS_CHANGED, dms.relinquishRole(N1, DID1).type()); 219 + assertEquals("wrong event:", Type.BACKUPS_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID1)).type());
219 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1)); 220 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
220 221
221 //NONE - nothing happens 222 //NONE - nothing happens
222 - assertEquals("wrong event:", Type.BACKUPS_CHANGED, dms.relinquishRole(N1, DID2).type()); 223 + assertEquals("wrong event:", Type.BACKUPS_CHANGED, Futures.getUnchecked(dms.relinquishRole(N1, DID2)).type());
223 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2)); 224 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
224 225
225 } 226 }
......
...@@ -27,6 +27,7 @@ import java.util.List; ...@@ -27,6 +27,7 @@ import java.util.List;
27 import java.util.Map; 27 import java.util.Map;
28 import java.util.Objects; 28 import java.util.Objects;
29 import java.util.Set; 29 import java.util.Set;
30 +import java.util.concurrent.CompletableFuture;
30 import java.util.concurrent.atomic.AtomicInteger; 31 import java.util.concurrent.atomic.AtomicInteger;
31 32
32 import org.apache.felix.scr.annotations.Activate; 33 import org.apache.felix.scr.annotations.Activate;
...@@ -143,13 +144,13 @@ public class SimpleMastershipStore ...@@ -143,13 +144,13 @@ public class SimpleMastershipStore
143 } 144 }
144 145
145 @Override 146 @Override
146 - public synchronized MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 147 + public synchronized CompletableFuture<MastershipEvent> setMaster(NodeId nodeId, DeviceId deviceId) {
147 148
148 MastershipRole role = getRole(nodeId, deviceId); 149 MastershipRole role = getRole(nodeId, deviceId);
149 switch (role) { 150 switch (role) {
150 case MASTER: 151 case MASTER:
151 // no-op 152 // no-op
152 - return null; 153 + return CompletableFuture.completedFuture(null);
153 case STANDBY: 154 case STANDBY:
154 case NONE: 155 case NONE:
155 NodeId prevMaster = masterMap.put(deviceId, nodeId); 156 NodeId prevMaster = masterMap.put(deviceId, nodeId);
...@@ -162,8 +163,8 @@ public class SimpleMastershipStore ...@@ -162,8 +163,8 @@ public class SimpleMastershipStore
162 return null; 163 return null;
163 } 164 }
164 165
165 - return new MastershipEvent(MASTER_CHANGED, deviceId, 166 + return CompletableFuture.completedFuture(
166 - getNodes(deviceId)); 167 + new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
167 } 168 }
168 169
169 @Override 170 @Override
...@@ -285,7 +286,7 @@ public class SimpleMastershipStore ...@@ -285,7 +286,7 @@ public class SimpleMastershipStore
285 } 286 }
286 287
287 @Override 288 @Override
288 - public synchronized MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) { 289 + public synchronized CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId) {
289 MastershipRole role = getRole(nodeId, deviceId); 290 MastershipRole role = getRole(nodeId, deviceId);
290 switch (role) { 291 switch (role) {
291 case MASTER: 292 case MASTER:
...@@ -294,22 +295,22 @@ public class SimpleMastershipStore ...@@ -294,22 +295,22 @@ public class SimpleMastershipStore
294 // no master alternative 295 // no master alternative
295 masterMap.remove(deviceId); 296 masterMap.remove(deviceId);
296 // TODO: Should there be new event type for no MASTER? 297 // TODO: Should there be new event type for no MASTER?
297 - return new MastershipEvent(MASTER_CHANGED, deviceId, 298 + return CompletableFuture.completedFuture(
298 - getNodes(deviceId)); 299 + new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
299 } else { 300 } else {
300 NodeId prevMaster = masterMap.put(deviceId, backup); 301 NodeId prevMaster = masterMap.put(deviceId, backup);
301 incrementTerm(deviceId); 302 incrementTerm(deviceId);
302 addToBackup(deviceId, prevMaster); 303 addToBackup(deviceId, prevMaster);
303 - return new MastershipEvent(MASTER_CHANGED, deviceId, 304 + return CompletableFuture.completedFuture(
304 - getNodes(deviceId)); 305 + new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
305 } 306 }
306 307
307 case STANDBY: 308 case STANDBY:
308 case NONE: 309 case NONE:
309 boolean modified = addToBackup(deviceId, nodeId); 310 boolean modified = addToBackup(deviceId, nodeId);
310 if (modified) { 311 if (modified) {
311 - return new MastershipEvent(BACKUPS_CHANGED, deviceId, 312 + return CompletableFuture.completedFuture(
312 - getNodes(deviceId)); 313 + new MastershipEvent(BACKUPS_CHANGED, deviceId, getNodes(deviceId)));
313 } 314 }
314 break; 315 break;
315 316
...@@ -335,20 +336,20 @@ public class SimpleMastershipStore ...@@ -335,20 +336,20 @@ public class SimpleMastershipStore
335 } 336 }
336 337
337 @Override 338 @Override
338 - public synchronized MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) { 339 + public synchronized CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId) {
339 MastershipRole role = getRole(nodeId, deviceId); 340 MastershipRole role = getRole(nodeId, deviceId);
340 switch (role) { 341 switch (role) {
341 case MASTER: 342 case MASTER:
342 NodeId backup = reelect(deviceId, nodeId); 343 NodeId backup = reelect(deviceId, nodeId);
343 masterMap.put(deviceId, backup); 344 masterMap.put(deviceId, backup);
344 incrementTerm(deviceId); 345 incrementTerm(deviceId);
345 - return new MastershipEvent(MASTER_CHANGED, deviceId, 346 + return CompletableFuture.completedFuture(
346 - getNodes(deviceId)); 347 + new MastershipEvent(MASTER_CHANGED, deviceId, getNodes(deviceId)));
347 348
348 case STANDBY: 349 case STANDBY:
349 if (removeFromBackups(deviceId, nodeId)) { 350 if (removeFromBackups(deviceId, nodeId)) {
350 - return new MastershipEvent(BACKUPS_CHANGED, deviceId, 351 + return CompletableFuture.completedFuture(
351 - getNodes(deviceId)); 352 + new MastershipEvent(BACKUPS_CHANGED, deviceId, getNodes(deviceId)));
352 } 353 }
353 break; 354 break;
354 355
...@@ -358,12 +359,12 @@ public class SimpleMastershipStore ...@@ -358,12 +359,12 @@ public class SimpleMastershipStore
358 default: 359 default:
359 log.warn("unknown Mastership Role {}", role); 360 log.warn("unknown Mastership Role {}", role);
360 } 361 }
361 - return null; 362 + return CompletableFuture.completedFuture(null);
362 } 363 }
363 364
364 @Override 365 @Override
365 public synchronized void relinquishAllRole(NodeId nodeId) { 366 public synchronized void relinquishAllRole(NodeId nodeId) {
366 - List<MastershipEvent> events = new ArrayList<>(); 367 + List<CompletableFuture<MastershipEvent>> eventFutures = new ArrayList<>();
367 Set<DeviceId> toRelinquish = new HashSet<>(); 368 Set<DeviceId> toRelinquish = new HashSet<>();
368 369
369 masterMap.entrySet().stream() 370 masterMap.entrySet().stream()
...@@ -375,12 +376,13 @@ public class SimpleMastershipStore ...@@ -375,12 +376,13 @@ public class SimpleMastershipStore
375 .forEach(entry -> toRelinquish.add(entry.getKey())); 376 .forEach(entry -> toRelinquish.add(entry.getKey()));
376 377
377 toRelinquish.forEach(deviceId -> { 378 toRelinquish.forEach(deviceId -> {
378 - MastershipEvent event = relinquishRole(nodeId, deviceId); 379 + eventFutures.add(relinquishRole(nodeId, deviceId));
379 - if (event != null) {
380 - events.add(event);
381 - }
382 }); 380 });
383 381
384 - notifyDelegate(events); 382 + eventFutures.forEach(future -> {
383 + future.whenComplete((event, error) -> {
384 + notifyDelegate(event);
385 + });
386 + });
385 } 387 }
386 } 388 }
......
...@@ -29,6 +29,7 @@ import org.onosproject.mastership.MastershipTerm; ...@@ -29,6 +29,7 @@ import org.onosproject.mastership.MastershipTerm;
29 import org.onosproject.net.DeviceId; 29 import org.onosproject.net.DeviceId;
30 30
31 import com.google.common.collect.Sets; 31 import com.google.common.collect.Sets;
32 +import com.google.common.util.concurrent.Futures;
32 33
33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertEquals;
34 import static org.junit.Assert.assertNull; 35 import static org.junit.Assert.assertNull;
...@@ -92,15 +93,15 @@ public class SimpleMastershipStoreTest { ...@@ -92,15 +93,15 @@ public class SimpleMastershipStoreTest {
92 @Test 93 @Test
93 public void setMaster() { 94 public void setMaster() {
94 put(DID1, N1, false, false); 95 put(DID1, N1, false, false);
95 - assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID1).type()); 96 + assertEquals("wrong event", MASTER_CHANGED, Futures.getUnchecked(sms.setMaster(N1, DID1)).type());
96 assertEquals("wrong role", MASTER, sms.getRole(N1, DID1)); 97 assertEquals("wrong role", MASTER, sms.getRole(N1, DID1));
97 //set node that's already master - should be ignored 98 //set node that's already master - should be ignored
98 - assertNull("wrong event", sms.setMaster(N1, DID1)); 99 + assertNull("wrong event", Futures.getUnchecked(sms.setMaster(N1, DID1)));
99 100
100 //set STANDBY to MASTER 101 //set STANDBY to MASTER
101 put(DID2, N1, false, true); 102 put(DID2, N1, false, true);
102 assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2)); 103 assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2));
103 - assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID2).type()); 104 + assertEquals("wrong event", MASTER_CHANGED, Futures.getUnchecked(sms.setMaster(N1, DID2)).type());
104 assertEquals("wrong role", MASTER, sms.getRole(N1, DID2)); 105 assertEquals("wrong role", MASTER, sms.getRole(N1, DID2));
105 } 106 }
106 107
...@@ -156,7 +157,7 @@ public class SimpleMastershipStoreTest { ...@@ -156,7 +157,7 @@ public class SimpleMastershipStoreTest {
156 157
157 //no backup, MASTER 158 //no backup, MASTER
158 put(DID1, N1, true, false); 159 put(DID1, N1, true, false);
159 - assertNull("expect no MASTER event", sms.setStandby(N1, DID1).roleInfo().master()); 160 + assertNull("expect no MASTER event", Futures.getUnchecked(sms.setStandby(N1, DID1)).roleInfo().master());
160 assertNull("wrong node", sms.masterMap.get(DID1)); 161 assertNull("wrong node", sms.masterMap.get(DID1));
161 162
162 //backup, switch 163 //backup, switch
...@@ -164,7 +165,7 @@ public class SimpleMastershipStoreTest { ...@@ -164,7 +165,7 @@ public class SimpleMastershipStoreTest {
164 put(DID1, N1, true, true); 165 put(DID1, N1, true, true);
165 put(DID1, N2, false, true); 166 put(DID1, N2, false, true);
166 put(DID2, N2, true, true); 167 put(DID2, N2, true, true);
167 - MastershipEvent event = sms.setStandby(N1, DID1); 168 + MastershipEvent event = Futures.getUnchecked(sms.setStandby(N1, DID1));
168 assertEquals("wrong event", MASTER_CHANGED, event.type()); 169 assertEquals("wrong event", MASTER_CHANGED, event.type());
169 assertEquals("wrong master", N2, event.roleInfo().master()); 170 assertEquals("wrong master", N2, event.roleInfo().master());
170 } 171 }
......