blob: e3b58ea149980559cb8f2df22813f2ab311957ac [file] [log] [blame]
Yoonseon Hana578d762017-05-08 13:42:02 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yoonseon Hana578d762017-05-08 13:42:02 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.incubator.store.virtual.impl;
18
Yoonseon Hana3277012017-05-22 12:26:21 -070019import com.google.common.collect.ImmutableList;
Jordan Halterman0a2bd452018-06-13 17:24:58 -070020import com.google.common.collect.ImmutableMap;
Yoonseon Hana3277012017-05-22 12:26:21 -070021import com.google.common.collect.ImmutableSet;
Yoonseon Hana3277012017-05-22 12:26:21 -070022import org.onlab.packet.IpAddress;
23import org.onosproject.cluster.ClusterEventListener;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.ControllerNode;
26import org.onosproject.cluster.DefaultControllerNode;
Jordan Halterman00e92da2018-05-22 23:05:52 -070027import org.onosproject.cluster.Node;
Yoonseon Hana578d762017-05-08 13:42:02 -070028import org.onosproject.cluster.NodeId;
29import org.onosproject.cluster.RoleInfo;
Jordan Haltermanf70bf462017-07-29 13:12:00 -070030import org.onosproject.core.Version;
31import org.onosproject.core.VersionService;
Yoonseon Hana578d762017-05-08 13:42:02 -070032import org.onosproject.incubator.net.virtual.NetworkId;
33import org.onosproject.incubator.net.virtual.VirtualNetworkMastershipStore;
34import org.onosproject.mastership.MastershipEvent;
Jordan Halterman0a2bd452018-06-13 17:24:58 -070035import org.onosproject.mastership.MastershipInfo;
Yoonseon Hana578d762017-05-08 13:42:02 -070036import org.onosproject.mastership.MastershipStoreDelegate;
37import org.onosproject.mastership.MastershipTerm;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.MastershipRole;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070040import org.osgi.service.component.annotations.Activate;
41import org.osgi.service.component.annotations.Component;
42import org.osgi.service.component.annotations.Deactivate;
43import org.osgi.service.component.annotations.Reference;
44import org.osgi.service.component.annotations.ReferenceCardinality;
Yoonseon Hana578d762017-05-08 13:42:02 -070045import org.slf4j.Logger;
46
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -070047import java.time.Instant;
Yoonseon Hana3277012017-05-22 12:26:21 -070048import java.util.ArrayList;
49import java.util.Collections;
50import java.util.HashMap;
51import java.util.HashSet;
52import java.util.List;
53import java.util.Map;
54import java.util.Objects;
Jordan Halterman0a2bd452018-06-13 17:24:58 -070055import java.util.Optional;
Yoonseon Hana578d762017-05-08 13:42:02 -070056import java.util.Set;
57import java.util.concurrent.CompletableFuture;
Yoonseon Hana3277012017-05-22 12:26:21 -070058import java.util.concurrent.atomic.AtomicInteger;
Yoonseon Hana578d762017-05-08 13:42:02 -070059
Yoonseon Hana3277012017-05-22 12:26:21 -070060import static org.onosproject.mastership.MastershipEvent.Type.BACKUPS_CHANGED;
61import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
Yoonseon Hana578d762017-05-08 13:42:02 -070062import static org.slf4j.LoggerFactory.getLogger;
63
64/**
65 * Implementation of the virtual network mastership store to manage inventory of
66 * mastership using trivial in-memory implementation.
67 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068@Component(immediate = true, service = VirtualNetworkMastershipStore.class)
Yoonseon Hana578d762017-05-08 13:42:02 -070069public class SimpleVirtualMastershipStore
70 extends AbstractVirtualStore<MastershipEvent, MastershipStoreDelegate>
71 implements VirtualNetworkMastershipStore {
72
73 private final Logger log = getLogger(getClass());
74
Yoonseon Hana3277012017-05-22 12:26:21 -070075 private static final int NOTHING = 0;
76 private static final int INIT = 1;
77
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yoonseon Hana3277012017-05-22 12:26:21 -070079 protected ClusterService clusterService;
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jordan Haltermanf70bf462017-07-29 13:12:00 -070082 protected VersionService versionService;
83
Yoonseon Hana3277012017-05-22 12:26:21 -070084 //devices mapped to their masters, to emulate multiple nodes
85 protected final Map<NetworkId, Map<DeviceId, NodeId>> masterMapByNetwork =
86 new HashMap<>();
87 //emulate backups with pile of nodes
88 protected final Map<NetworkId, Map<DeviceId, List<NodeId>>> backupsByNetwork =
89 new HashMap<>();
90 //terms
91 protected final Map<NetworkId, Map<DeviceId, AtomicInteger>> termMapByNetwork =
92 new HashMap<>();
93
Yoonseon Hana578d762017-05-08 13:42:02 -070094 @Activate
95 public void activate() {
Yoonseon Hana3277012017-05-22 12:26:21 -070096 if (clusterService == null) {
97 clusterService = createFakeClusterService();
98 }
Yoonseon Hana578d762017-05-08 13:42:02 -070099 log.info("Started");
100 }
101
102 @Deactivate
103 public void deactivate() {
104 log.info("Stopped");
105 }
106
107 @Override
Yoonseon Hana3277012017-05-22 12:26:21 -0700108 public CompletableFuture<MastershipRole> requestRole(NetworkId networkId,
109 DeviceId deviceId) {
110 //query+possible reelection
111 NodeId node = clusterService.getLocalNode().id();
112 MastershipRole role = getRole(networkId, node, deviceId);
113
114 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
115
116 switch (role) {
117 case MASTER:
118 return CompletableFuture.completedFuture(MastershipRole.MASTER);
119 case STANDBY:
120 if (getMaster(networkId, deviceId) == null) {
121 // no master => become master
122 masterMap.put(deviceId, node);
123 incrementTerm(networkId, deviceId);
124 // remove from backup list
125 removeFromBackups(networkId, deviceId, node);
126 notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700127 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700128 return CompletableFuture.completedFuture(MastershipRole.MASTER);
129 }
130 return CompletableFuture.completedFuture(MastershipRole.STANDBY);
131 case NONE:
132 if (getMaster(networkId, deviceId) == null) {
133 // no master => become master
134 masterMap.put(deviceId, node);
135 incrementTerm(networkId, deviceId);
136 notifyDelegate(networkId, new MastershipEvent(MASTER_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700137 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700138 return CompletableFuture.completedFuture(MastershipRole.MASTER);
139 }
140 // add to backup list
141 if (addToBackup(networkId, deviceId, node)) {
142 notifyDelegate(networkId, new MastershipEvent(BACKUPS_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700143 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700144 }
145 return CompletableFuture.completedFuture(MastershipRole.STANDBY);
146 default:
147 log.warn("unknown Mastership Role {}", role);
148 }
149 return CompletableFuture.completedFuture(role);
Yoonseon Hana578d762017-05-08 13:42:02 -0700150 }
151
152 @Override
153 public MastershipRole getRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700154 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
155 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
156
157 //just query
158 NodeId current = masterMap.get(deviceId);
159 MastershipRole role;
160
161 if (current != null && current.equals(nodeId)) {
162 return MastershipRole.MASTER;
163 }
164
165 if (backups.getOrDefault(deviceId, Collections.emptyList()).contains(nodeId)) {
166 role = MastershipRole.STANDBY;
167 } else {
168 role = MastershipRole.NONE;
169 }
170 return role;
Yoonseon Hana578d762017-05-08 13:42:02 -0700171 }
172
173 @Override
174 public NodeId getMaster(NetworkId networkId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700175 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
176 return masterMap.get(deviceId);
Yoonseon Hana578d762017-05-08 13:42:02 -0700177 }
178
179 @Override
180 public RoleInfo getNodes(NetworkId networkId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700181 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
182 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
183
184 return new RoleInfo(masterMap.get(deviceId),
185 backups.getOrDefault(deviceId, ImmutableList.of()));
Yoonseon Hana578d762017-05-08 13:42:02 -0700186 }
187
188 @Override
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700189 public MastershipInfo getMastership(NetworkId networkId, DeviceId deviceId) {
190 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
191 Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
192 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
193 ImmutableMap.Builder<NodeId, MastershipRole> roleBuilder = ImmutableMap.builder();
194 NodeId master = masterMap.get(deviceId);
195 if (master != null) {
196 roleBuilder.put(master, MastershipRole.MASTER);
197 }
Ray Milkey30f35b22018-06-21 14:56:47 -0700198 backups.getOrDefault(deviceId, Collections.emptyList())
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700199 .forEach(nodeId -> roleBuilder.put(nodeId, MastershipRole.STANDBY));
200 clusterService.getNodes().stream()
201 .filter(node -> !masterMap.containsValue(node.id()))
Ray Milkey30f35b22018-06-21 14:56:47 -0700202 .filter(node -> !backups.get(deviceId).contains(node.id()))
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700203 .forEach(node -> roleBuilder.put(node.id(), MastershipRole.NONE));
204 return new MastershipInfo(
205 termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING)).get(),
206 Optional.ofNullable(master),
207 roleBuilder.build());
208 }
209
210 @Override
Yoonseon Hana578d762017-05-08 13:42:02 -0700211 public Set<DeviceId> getDevices(NetworkId networkId, NodeId nodeId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700212 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
213
214 Set<DeviceId> ids = new HashSet<>();
215 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
216 if (Objects.equals(d.getValue(), nodeId)) {
217 ids.add(d.getKey());
218 }
219 }
220 return ids;
Yoonseon Hana578d762017-05-08 13:42:02 -0700221 }
222
223 @Override
Yoonseon Hana3277012017-05-22 12:26:21 -0700224 public synchronized CompletableFuture<MastershipEvent> setMaster(NetworkId networkId,
Yoonseon Hana578d762017-05-08 13:42:02 -0700225 NodeId nodeId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700226 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
227
228 MastershipRole role = getRole(networkId, nodeId, deviceId);
229 switch (role) {
230 case MASTER:
231 // no-op
232 return CompletableFuture.completedFuture(null);
233 case STANDBY:
234 case NONE:
235 NodeId prevMaster = masterMap.put(deviceId, nodeId);
236 incrementTerm(networkId, deviceId);
237 removeFromBackups(networkId, deviceId, nodeId);
238 addToBackup(networkId, deviceId, prevMaster);
239 break;
240 default:
241 log.warn("unknown Mastership Role {}", role);
242 return null;
243 }
244
245 return CompletableFuture.completedFuture(
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700246 new MastershipEvent(MASTER_CHANGED, deviceId, getMastership(networkId, deviceId)));
Yoonseon Hana578d762017-05-08 13:42:02 -0700247 }
248
249 @Override
250 public MastershipTerm getTermFor(NetworkId networkId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700251 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
252 Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
253
254 if ((termMap.get(deviceId) == null)) {
255 return MastershipTerm.of(masterMap.get(deviceId), NOTHING);
256 }
257 return MastershipTerm.of(
258 masterMap.get(deviceId), termMap.get(deviceId).get());
Yoonseon Hana578d762017-05-08 13:42:02 -0700259 }
260
261 @Override
262 public CompletableFuture<MastershipEvent> setStandby(NetworkId networkId,
263 NodeId nodeId, DeviceId deviceId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700264 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
265
266 MastershipRole role = getRole(networkId, nodeId, deviceId);
267 switch (role) {
268 case MASTER:
269 NodeId backup = reelect(networkId, deviceId, nodeId);
270 if (backup == null) {
271 // no master alternative
272 masterMap.remove(deviceId);
273 // TODO: Should there be new event type for no MASTER?
274 return CompletableFuture.completedFuture(
275 new MastershipEvent(MASTER_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700276 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700277 } else {
278 NodeId prevMaster = masterMap.put(deviceId, backup);
279 incrementTerm(networkId, deviceId);
280 addToBackup(networkId, deviceId, prevMaster);
281 return CompletableFuture.completedFuture(
282 new MastershipEvent(MASTER_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700283 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700284 }
285
286 case STANDBY:
287 case NONE:
288 boolean modified = addToBackup(networkId, deviceId, nodeId);
289 if (modified) {
290 return CompletableFuture.completedFuture(
291 new MastershipEvent(BACKUPS_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700292 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700293 }
294 break;
295
296 default:
297 log.warn("unknown Mastership Role {}", role);
298 }
Yoonseon Hana578d762017-05-08 13:42:02 -0700299 return null;
300 }
301
Yoonseon Hana3277012017-05-22 12:26:21 -0700302
303 /**
304 * Dumbly selects next-available node that's not the current one.
305 * emulate leader election.
306 *
307 * @param networkId a virtual network identifier
308 * @param deviceId a virtual device identifier
309 * @param nodeId a nod identifier
310 * @return Next available node as a leader
311 */
312 private synchronized NodeId reelect(NetworkId networkId, DeviceId deviceId,
313 NodeId nodeId) {
314 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
315
316 List<NodeId> stbys = backups.getOrDefault(deviceId, Collections.emptyList());
317 NodeId backup = null;
318 for (NodeId n : stbys) {
319 if (!n.equals(nodeId)) {
320 backup = n;
321 break;
322 }
323 }
324 stbys.remove(backup);
325 return backup;
326 }
327
Yoonseon Hana578d762017-05-08 13:42:02 -0700328 @Override
Yoonseon Hana3277012017-05-22 12:26:21 -0700329 public synchronized CompletableFuture<MastershipEvent>
330 relinquishRole(NetworkId networkId, NodeId nodeId, DeviceId deviceId) {
331 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
332
333 MastershipRole role = getRole(networkId, nodeId, deviceId);
334 switch (role) {
335 case MASTER:
336 NodeId backup = reelect(networkId, deviceId, nodeId);
337 masterMap.put(deviceId, backup);
338 incrementTerm(networkId, deviceId);
339 return CompletableFuture.completedFuture(
340 new MastershipEvent(MASTER_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700341 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700342
343 case STANDBY:
344 if (removeFromBackups(networkId, deviceId, nodeId)) {
345 return CompletableFuture.completedFuture(
346 new MastershipEvent(BACKUPS_CHANGED, deviceId,
Jordan Halterman0a2bd452018-06-13 17:24:58 -0700347 getMastership(networkId, deviceId)));
Yoonseon Hana3277012017-05-22 12:26:21 -0700348 }
349 break;
350
351 case NONE:
352 break;
353
354 default:
355 log.warn("unknown Mastership Role {}", role);
356 }
357 return CompletableFuture.completedFuture(null);
Yoonseon Hana578d762017-05-08 13:42:02 -0700358 }
359
360 @Override
361 public void relinquishAllRole(NetworkId networkId, NodeId nodeId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700362 Map<DeviceId, NodeId> masterMap = getMasterMap(networkId);
363 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
Yoonseon Hana578d762017-05-08 13:42:02 -0700364
Yoonseon Hana3277012017-05-22 12:26:21 -0700365 List<CompletableFuture<MastershipEvent>> eventFutures = new ArrayList<>();
366 Set<DeviceId> toRelinquish = new HashSet<>();
367
368 masterMap.entrySet().stream()
369 .filter(entry -> nodeId.equals(entry.getValue()))
370 .forEach(entry -> toRelinquish.add(entry.getKey()));
371
372 backups.entrySet().stream()
373 .filter(entry -> entry.getValue().contains(nodeId))
374 .forEach(entry -> toRelinquish.add(entry.getKey()));
375
376 toRelinquish.forEach(deviceId -> eventFutures.add(
377 relinquishRole(networkId, nodeId, deviceId)));
378
379 eventFutures.forEach(future -> {
380 future.whenComplete((event, error) -> notifyDelegate(networkId, event));
381 });
382 }
383
384 /**
385 * Increase the term for a device, and store it.
386 *
387 * @param networkId a virtual network identifier
388 * @param deviceId a virtual device identifier
389 */
390 private synchronized void incrementTerm(NetworkId networkId, DeviceId deviceId) {
391 Map<DeviceId, AtomicInteger> termMap = getTermMap(networkId);
392
393 AtomicInteger term = termMap.getOrDefault(deviceId, new AtomicInteger(NOTHING));
394 term.incrementAndGet();
395 termMap.put(deviceId, term);
396 }
397
398 /**
399 * Remove backup node for a device.
400 *
401 * @param networkId a virtual network identifier
402 * @param deviceId a virtual device identifier
403 * @param nodeId a node identifier
404 * @return True if success
405 */
406 private synchronized boolean removeFromBackups(NetworkId networkId,
407 DeviceId deviceId, NodeId nodeId) {
408 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
409
410 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
411 boolean modified = stbys.remove(nodeId);
412 backups.put(deviceId, stbys);
413 return modified;
414 }
415
416 /**
417 * add to backup if not there already, silently ignores null node.
418 *
419 * @param networkId a virtual network identifier
420 * @param deviceId a virtual device identifier
421 * @param nodeId a node identifier
422 * @return True if success
423 */
424 private synchronized boolean addToBackup(NetworkId networkId,
425 DeviceId deviceId, NodeId nodeId) {
426 Map<DeviceId, List<NodeId>> backups = getBackups(networkId);
427
428 boolean modified = false;
429 List<NodeId> stbys = backups.getOrDefault(deviceId, new ArrayList<>());
430 if (nodeId != null && !stbys.contains(nodeId)) {
431 stbys.add(nodeId);
432 backups.put(deviceId, stbys);
433 modified = true;
434 }
435 return modified;
436 }
437
438 /**
439 * Returns deviceId-master map for a specified virtual network.
440 *
441 * @param networkId a virtual network identifier
442 * @return DeviceId-master map of a given virtual network.
443 */
444 private Map<DeviceId, NodeId> getMasterMap(NetworkId networkId) {
445 return masterMapByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
446 }
447
448 /**
449 * Returns deviceId-backups map for a specified virtual network.
450 *
451 * @param networkId a virtual network identifier
452 * @return DeviceId-backups map of a given virtual network.
453 */
454 private Map<DeviceId, List<NodeId>> getBackups(NetworkId networkId) {
455 return backupsByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
456 }
457
458 /**
459 * Returns deviceId-terms map for a specified virtual network.
460 *
461 * @param networkId a virtual network identifier
462 * @return DeviceId-terms map of a given virtual network.
463 */
464 private Map<DeviceId, AtomicInteger> getTermMap(NetworkId networkId) {
465 return termMapByNetwork.computeIfAbsent(networkId, k -> new HashMap<>());
466 }
467
468 /**
469 * Returns a fake cluster service for a test purpose only.
470 *
471 * @return a fake cluster service
472 */
473 private ClusterService createFakeClusterService() {
474 // just for ease of unit test
475 final ControllerNode instance =
476 new DefaultControllerNode(new NodeId("local"),
477 IpAddress.valueOf("127.0.0.1"));
478
479 ClusterService faceClusterService = new ClusterService() {
480
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700481 private final Instant creationTime = Instant.now();
Yoonseon Hana3277012017-05-22 12:26:21 -0700482
483 @Override
484 public ControllerNode getLocalNode() {
485 return instance;
486 }
487
488 @Override
489 public Set<ControllerNode> getNodes() {
490 return ImmutableSet.of(instance);
491 }
492
493 @Override
Jordan Halterman00e92da2018-05-22 23:05:52 -0700494 public Set<Node> getConsensusNodes() {
495 return ImmutableSet.of();
496 }
497
498 @Override
Yoonseon Hana3277012017-05-22 12:26:21 -0700499 public ControllerNode getNode(NodeId nodeId) {
500 if (instance.id().equals(nodeId)) {
501 return instance;
502 }
503 return null;
504 }
505
506 @Override
507 public ControllerNode.State getState(NodeId nodeId) {
508 if (instance.id().equals(nodeId)) {
509 return ControllerNode.State.ACTIVE;
510 } else {
511 return ControllerNode.State.INACTIVE;
512 }
513 }
514
515 @Override
Jordan Haltermanf70bf462017-07-29 13:12:00 -0700516 public Version getVersion(NodeId nodeId) {
517 if (instance.id().equals(nodeId)) {
518 return versionService.version();
519 }
520 return null;
521 }
522
523 @Override
Yuta HIGUCHI0c47d532017-08-18 23:16:35 -0700524 public Instant getLastUpdatedInstant(NodeId nodeId) {
Yoonseon Hana3277012017-05-22 12:26:21 -0700525 return creationTime;
526 }
527
528 @Override
529 public void addListener(ClusterEventListener listener) {
530 }
531
532 @Override
533 public void removeListener(ClusterEventListener listener) {
534 }
535 };
536 return faceClusterService;
Yoonseon Hana578d762017-05-08 13:42:02 -0700537 }
538}