Thomas Vachuska
Committed by Gerrit Code Review

Added ability to track whether or not node has all components running fully.

Change-Id: Ib2b90c7a842976a3b3a9711367fa1eed43103b17
...@@ -22,7 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; ...@@ -22,7 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
22 import org.apache.karaf.shell.commands.Command; 22 import org.apache.karaf.shell.commands.Command;
23 import org.joda.time.DateTime; 23 import org.joda.time.DateTime;
24 import org.onlab.util.Tools; 24 import org.onlab.util.Tools;
25 -import org.onosproject.cluster.ClusterService; 25 +import org.onosproject.cluster.ClusterAdminService;
26 import org.onosproject.cluster.ControllerNode; 26 import org.onosproject.cluster.ControllerNode;
27 import org.onosproject.utils.Comparators; 27 import org.onosproject.utils.Comparators;
28 28
...@@ -36,15 +36,14 @@ import static com.google.common.collect.Lists.newArrayList; ...@@ -36,15 +36,14 @@ import static com.google.common.collect.Lists.newArrayList;
36 * Lists all controller cluster nodes. 36 * Lists all controller cluster nodes.
37 */ 37 */
38 @Command(scope = "onos", name = "nodes", 38 @Command(scope = "onos", name = "nodes",
39 - description = "Lists all controller cluster nodes") 39 + description = "Lists all controller cluster nodes")
40 public class NodesListCommand extends AbstractShellCommand { 40 public class NodesListCommand extends AbstractShellCommand {
41 41
42 - private static final String FMT = 42 + private static final String FMT = "id=%s, address=%s:%s, state=%s, updated=%s %s";
43 - "id=%s, address=%s:%s, state=%s, updated=%s %s";
44 43
45 @Override 44 @Override
46 protected void execute() { 45 protected void execute() {
47 - ClusterService service = get(ClusterService.class); 46 + ClusterAdminService service = get(ClusterAdminService.class);
48 List<ControllerNode> nodes = newArrayList(service.getNodes()); 47 List<ControllerNode> nodes = newArrayList(service.getNodes());
49 Collections.sort(nodes, Comparators.NODE_COMPARATOR); 48 Collections.sort(nodes, Comparators.NODE_COMPARATOR);
50 if (outputJson()) { 49 if (outputJson()) {
...@@ -58,26 +57,24 @@ public class NodesListCommand extends AbstractShellCommand { ...@@ -58,26 +57,24 @@ public class NodesListCommand extends AbstractShellCommand {
58 timeAgo = Tools.timeAgo(lastUpdated.getMillis()); 57 timeAgo = Tools.timeAgo(lastUpdated.getMillis());
59 } 58 }
60 print(FMT, node.id(), node.ip(), node.tcpPort(), 59 print(FMT, node.id(), node.ip(), node.tcpPort(),
61 - service.getState(node.id()), 60 + service.getState(node.id()), timeAgo,
62 - timeAgo,
63 node.equals(self) ? "*" : ""); 61 node.equals(self) ? "*" : "");
64 } 62 }
65 } 63 }
66 } 64 }
67 65
68 // Produces JSON structure. 66 // Produces JSON structure.
69 - private JsonNode json(ClusterService service, List<ControllerNode> nodes) { 67 + private JsonNode json(ClusterAdminService service, List<ControllerNode> nodes) {
70 ObjectMapper mapper = new ObjectMapper(); 68 ObjectMapper mapper = new ObjectMapper();
71 ArrayNode result = mapper.createArrayNode(); 69 ArrayNode result = mapper.createArrayNode();
72 ControllerNode self = service.getLocalNode(); 70 ControllerNode self = service.getLocalNode();
73 for (ControllerNode node : nodes) { 71 for (ControllerNode node : nodes) {
74 ControllerNode.State nodeState = service.getState(node.id()); 72 ControllerNode.State nodeState = service.getState(node.id());
75 ObjectNode newNode = mapper.createObjectNode() 73 ObjectNode newNode = mapper.createObjectNode()
76 - .put("id", node.id().toString()) 74 + .put("id", node.id().toString())
77 - .put("ip", node.ip().toString()) 75 + .put("ip", node.ip().toString())
78 - .put("tcpPort", node.tcpPort()) 76 + .put("tcpPort", node.tcpPort())
79 - .put("self", node.equals(self)); 77 + .put("self", node.equals(self));
80 -
81 if (nodeState != null) { 78 if (nodeState != null) {
82 newNode.put("state", nodeState.toString()); 79 newNode.put("state", nodeState.toString());
83 } 80 }
......
...@@ -49,7 +49,7 @@ public class SummaryCommand extends AbstractShellCommand { ...@@ -49,7 +49,7 @@ public class SummaryCommand extends AbstractShellCommand {
49 for (final ControllerNode node : nodes) { 49 for (final ControllerNode node : nodes) {
50 final ControllerNode.State nodeState = 50 final ControllerNode.State nodeState =
51 get(ClusterService.class).getState(node.id()); 51 get(ClusterService.class).getState(node.id());
52 - if (nodeState == ControllerNode.State.ACTIVE) { 52 + if (nodeState.isActive()) {
53 nodeCount++; 53 nodeCount++;
54 } 54 }
55 } 55 }
......
...@@ -22,7 +22,7 @@ import java.util.Set; ...@@ -22,7 +22,7 @@ import java.util.Set;
22 /** 22 /**
23 * Service for administering the cluster node membership. 23 * Service for administering the cluster node membership.
24 */ 24 */
25 -public interface ClusterAdminService { 25 +public interface ClusterAdminService extends ClusterService {
26 26
27 /** 27 /**
28 * Forms cluster configuration based on the specified set of node 28 * Forms cluster configuration based on the specified set of node
...@@ -50,4 +50,11 @@ public interface ClusterAdminService { ...@@ -50,4 +50,11 @@ public interface ClusterAdminService {
50 */ 50 */
51 void removeNode(NodeId nodeId); 51 void removeNode(NodeId nodeId);
52 52
53 + /**
54 + * Marks the current node as fully started or not.
55 + *
56 + * @param started true indicates all components have been started
57 + */
58 + void markFullyStarted(boolean started);
59 +
53 } 60 }
......
...@@ -50,7 +50,9 @@ public interface ClusterService ...@@ -50,7 +50,9 @@ public interface ClusterService
50 ControllerNode getNode(NodeId nodeId); 50 ControllerNode getNode(NodeId nodeId);
51 51
52 /** 52 /**
53 - * Returns the availability state of the specified controller node. 53 + * Returns the availability state of the specified controller node. Note
54 + * that this does not imply that all the core and application components
55 + * have been fully activated; only that the node has joined the cluster.
54 * 56 *
55 * @param nodeId controller node identifier 57 * @param nodeId controller node identifier
56 * @return availability state 58 * @return availability state
......
...@@ -57,6 +57,13 @@ public interface ClusterStore extends Store<ClusterEvent, ClusterStoreDelegate> ...@@ -57,6 +57,13 @@ public interface ClusterStore extends Store<ClusterEvent, ClusterStoreDelegate>
57 ControllerNode.State getState(NodeId nodeId); 57 ControllerNode.State getState(NodeId nodeId);
58 58
59 /** 59 /**
60 + * Marks the current node as fully started.
61 + *
62 + * @param started true indicates all components have been started
63 + */
64 + void markFullyStarted(boolean started);
65 +
66 + /**
60 * Returns the system when the availability state was last updated. 67 * Returns the system when the availability state was last updated.
61 * 68 *
62 * @param nodeId controller node identifier 69 * @param nodeId controller node identifier
......
...@@ -25,6 +25,12 @@ public interface ControllerNode { ...@@ -25,6 +25,12 @@ public interface ControllerNode {
25 /** Represents the operational state of the instance. */ 25 /** Represents the operational state of the instance. */
26 enum State { 26 enum State {
27 /** 27 /**
28 + * Signifies that the instance is active and that all components are
29 + * operating normally.
30 + */
31 + READY,
32 +
33 + /**
28 * Signifies that the instance is active and operating normally. 34 * Signifies that the instance is active and operating normally.
29 */ 35 */
30 ACTIVE, 36 ACTIVE,
...@@ -33,7 +39,25 @@ public interface ControllerNode { ...@@ -33,7 +39,25 @@ public interface ControllerNode {
33 * Signifies that the instance is inactive, which means either down or 39 * Signifies that the instance is inactive, which means either down or
34 * up, but not operational. 40 * up, but not operational.
35 */ 41 */
36 - INACTIVE 42 + INACTIVE;
43 +
44 + /**
45 + * Indicates whether the state represents node which is active or ready.
46 + *
47 + * @return true if active or ready
48 + */
49 + public boolean isActive() {
50 + return this == ACTIVE || this == READY;
51 + }
52 +
53 + /**
54 + * Indicates whether the state represents a node which is ready.
55 + *
56 + * @return true if active and ready
57 + */
58 + public boolean isReady() {
59 + return this == READY;
60 + }
37 } 61 }
38 62
39 /** 63 /**
......
...@@ -67,6 +67,7 @@ public class SimpleClusterStore ...@@ -67,6 +67,7 @@ public class SimpleClusterStore
67 protected EventDeliveryService eventDispatcher; 67 protected EventDeliveryService eventDispatcher;
68 68
69 private ListenerRegistry<IntentPartitionEvent, IntentPartitionEventListener> listenerRegistry; 69 private ListenerRegistry<IntentPartitionEvent, IntentPartitionEventListener> listenerRegistry;
70 + private boolean started = false;
70 71
71 @Activate 72 @Activate
72 public void activate() { 73 public void activate() {
...@@ -106,6 +107,11 @@ public class SimpleClusterStore ...@@ -106,6 +107,11 @@ public class SimpleClusterStore
106 } 107 }
107 108
108 @Override 109 @Override
110 + public void markFullyStarted(boolean started) {
111 + this.started = started;
112 + }
113 +
114 + @Override
109 public DateTime getLastUpdated(NodeId nodeId) { 115 public DateTime getLastUpdated(NodeId nodeId) {
110 return creationTime; 116 return creationTime;
111 } 117 }
......
...@@ -80,6 +80,11 @@ ...@@ -80,6 +80,11 @@
80 </dependency> 80 </dependency>
81 81
82 <dependency> 82 <dependency>
83 + <groupId>org.osgi</groupId>
84 + <artifactId>org.osgi.core</artifactId>
85 + </dependency>
86 +
87 + <dependency>
83 <groupId>org.onosproject</groupId> 88 <groupId>org.onosproject</groupId>
84 <artifactId>onos-incubator-api</artifactId> 89 <artifactId>onos-incubator-api</artifactId>
85 </dependency> 90 </dependency>
...@@ -93,6 +98,11 @@ ...@@ -93,6 +98,11 @@
93 <groupId>org.apache.karaf.system</groupId> 98 <groupId>org.apache.karaf.system</groupId>
94 <artifactId>org.apache.karaf.system.core</artifactId> 99 <artifactId>org.apache.karaf.system.core</artifactId>
95 </dependency> 100 </dependency>
101 +
102 + <dependency>
103 + <groupId>org.apache.felix</groupId>
104 + <artifactId>org.apache.felix.scr</artifactId>
105 + </dependency>
96 </dependencies> 106 </dependencies>
97 107
98 </project> 108 </project>
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
15 */ 15 */
16 package org.onosproject.cluster.impl; 16 package org.onosproject.cluster.impl;
17 17
18 +import com.google.common.collect.Collections2;
19 +import com.google.common.collect.Sets;
18 import org.apache.felix.scr.annotations.Activate; 20 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 21 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 22 import org.apache.felix.scr.annotations.Deactivate;
...@@ -44,9 +46,6 @@ import org.onosproject.cluster.PartitionId; ...@@ -44,9 +46,6 @@ import org.onosproject.cluster.PartitionId;
44 import org.onosproject.event.AbstractListenerManager; 46 import org.onosproject.event.AbstractListenerManager;
45 import org.slf4j.Logger; 47 import org.slf4j.Logger;
46 48
47 -import com.google.common.collect.Collections2;
48 -import com.google.common.collect.Sets;
49 -
50 import java.util.ArrayList; 49 import java.util.ArrayList;
51 import java.util.Collection; 50 import java.util.Collection;
52 import java.util.Collections; 51 import java.util.Collections;
...@@ -58,8 +57,8 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -58,8 +57,8 @@ import java.util.concurrent.atomic.AtomicReference;
58 import static com.google.common.base.Preconditions.checkArgument; 57 import static com.google.common.base.Preconditions.checkArgument;
59 import static com.google.common.base.Preconditions.checkNotNull; 58 import static com.google.common.base.Preconditions.checkNotNull;
60 import static org.onosproject.security.AppGuard.checkPermission; 59 import static org.onosproject.security.AppGuard.checkPermission;
60 +import static org.onosproject.security.AppPermission.Type.CLUSTER_READ;
61 import static org.slf4j.LoggerFactory.getLogger; 61 import static org.slf4j.LoggerFactory.getLogger;
62 -import static org.onosproject.security.AppPermission.Type.*;
63 62
64 /** 63 /**
65 * Implementation of the cluster service. 64 * Implementation of the cluster service.
...@@ -133,6 +132,10 @@ public class ClusterManager ...@@ -133,6 +132,10 @@ public class ClusterManager
133 return store.getState(nodeId); 132 return store.getState(nodeId);
134 } 133 }
135 134
135 + @Override
136 + public void markFullyStarted(boolean started) {
137 + store.markFullyStarted(started);
138 + }
136 139
137 @Override 140 @Override
138 public DateTime getLastUpdated(NodeId nodeId) { 141 public DateTime getLastUpdated(NodeId nodeId) {
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
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 +
17 +package org.onosproject.cluster.impl;
18 +
19 +import org.apache.felix.scr.Component;
20 +import org.apache.felix.scr.ScrService;
21 +import org.apache.felix.scr.annotations.Activate;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.karaf.features.Feature;
26 +import org.apache.karaf.features.FeaturesService;
27 +import org.onlab.util.SharedScheduledExecutors;
28 +import org.onosproject.cluster.ClusterAdminService;
29 +import org.osgi.framework.Bundle;
30 +import org.osgi.framework.BundleContext;
31 +import org.osgi.service.component.ComponentContext;
32 +import org.slf4j.Logger;
33 +import org.slf4j.LoggerFactory;
34 +
35 +import java.util.concurrent.ScheduledFuture;
36 +import java.util.concurrent.TimeUnit;
37 +
38 +/**
39 + * Monitors the system to make sure that all bundles and their components
40 + * are properly activated and keeps the cluster node service appropriately
41 + * updated.
42 + */
43 +@org.apache.felix.scr.annotations.Component(immediate = true)
44 +public class ComponentsMonitor {
45 +
46 + private Logger log = LoggerFactory.getLogger(getClass());
47 +
48 + private static final long PERIOD = 2500;
49 +
50 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
51 + protected FeaturesService featuresService;
52 +
53 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
54 + protected ScrService scrService;
55 +
56 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
57 + protected ClusterAdminService clusterAdminService;
58 +
59 + private BundleContext bundleContext;
60 + private ScheduledFuture<?> poller;
61 +
62 + @Activate
63 + protected void activate(ComponentContext context) {
64 + bundleContext = context.getBundleContext();
65 + poller = SharedScheduledExecutors.getSingleThreadExecutor()
66 + .scheduleAtFixedRate(this::checkStartedState, PERIOD,
67 + PERIOD, TimeUnit.MILLISECONDS);
68 + log.info("Started");
69 + }
70 +
71 + @Deactivate
72 + protected void deactivate() {
73 + poller.cancel(false);
74 + log.info("Stopped");
75 + }
76 +
77 + private void checkStartedState() {
78 + clusterAdminService.markFullyStarted(isFullyStarted());
79 + }
80 +
81 + /**
82 + * Scans the system to make sure that all bundles and their components
83 + * are fully started.
84 + *
85 + * @return true if all bundles and their components are active
86 + */
87 + private boolean isFullyStarted() {
88 + for (Feature feature : featuresService.listInstalledFeatures()) {
89 + if (!isFullyStarted(feature)) {
90 + return false;
91 + }
92 + }
93 + return true;
94 + }
95 +
96 + private boolean isFullyStarted(Feature feature) {
97 + return feature.getBundles().stream()
98 + .map(info -> bundleContext.getBundle(info.getLocation()))
99 + .allMatch(this::isFullyStarted);
100 + }
101 +
102 + private boolean isFullyStarted(Bundle bundle) {
103 + Component[] components = scrService.getComponents(bundle);
104 + if (components != null) {
105 + for (Component component : components) {
106 + if (!isFullyStarted(component)) {
107 + return false;
108 + }
109 + }
110 + }
111 + return true;
112 + }
113 +
114 + private boolean isFullyStarted(Component component) {
115 + int state = component.getState();
116 + return state == Component.STATE_ACTIVE || state == Component.STATE_DISABLED ||
117 + (state == Component.STATE_REGISTERED && !component.isImmediate());
118 + }
119 +
120 +}
...@@ -76,7 +76,7 @@ public class LeadershipManager ...@@ -76,7 +76,7 @@ public class LeadershipManager
76 deadlockDetector.scheduleWithFixedDelay(() -> clusterService.getNodes() 76 deadlockDetector.scheduleWithFixedDelay(() -> clusterService.getNodes()
77 .stream() 77 .stream()
78 .map(ControllerNode::id) 78 .map(ControllerNode::id)
79 - .filter(id -> clusterService.getState(id) != ControllerNode.State.ACTIVE) 79 + .filter(id -> !clusterService.getState(id).isActive())
80 .forEach(this::unregister), 0, 2, TimeUnit.SECONDS); 80 .forEach(this::unregister), 0, 2, TimeUnit.SECONDS);
81 log.info("Started"); 81 log.info("Started");
82 } 82 }
......
...@@ -30,8 +30,8 @@ import org.onosproject.cluster.ClusterService; ...@@ -30,8 +30,8 @@ import org.onosproject.cluster.ClusterService;
30 import org.onosproject.cluster.ControllerNode; 30 import org.onosproject.cluster.ControllerNode;
31 import org.onosproject.cluster.NodeId; 31 import org.onosproject.cluster.NodeId;
32 import org.onosproject.cluster.RoleInfo; 32 import org.onosproject.cluster.RoleInfo;
33 -import org.onosproject.event.AbstractListenerManager;
34 import org.onosproject.core.MetricsHelper; 33 import org.onosproject.core.MetricsHelper;
34 +import org.onosproject.event.AbstractListenerManager;
35 import org.onosproject.mastership.MastershipAdminService; 35 import org.onosproject.mastership.MastershipAdminService;
36 import org.onosproject.mastership.MastershipEvent; 36 import org.onosproject.mastership.MastershipEvent;
37 import org.onosproject.mastership.MastershipListener; 37 import org.onosproject.mastership.MastershipListener;
...@@ -57,11 +57,11 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -57,11 +57,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
57 import static com.google.common.collect.Lists.newArrayList; 57 import static com.google.common.collect.Lists.newArrayList;
58 import static org.onlab.metrics.MetricsUtil.startTimer; 58 import static org.onlab.metrics.MetricsUtil.startTimer;
59 import static org.onlab.metrics.MetricsUtil.stopTimer; 59 import static org.onlab.metrics.MetricsUtil.stopTimer;
60 -import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
61 import static org.onosproject.net.MastershipRole.MASTER; 60 import static org.onosproject.net.MastershipRole.MASTER;
62 import static org.onosproject.security.AppGuard.checkPermission; 61 import static org.onosproject.security.AppGuard.checkPermission;
62 +import static org.onosproject.security.AppPermission.Type.CLUSTER_READ;
63 +import static org.onosproject.security.AppPermission.Type.CLUSTER_WRITE;
63 import static org.slf4j.LoggerFactory.getLogger; 64 import static org.slf4j.LoggerFactory.getLogger;
64 -import static org.onosproject.security.AppPermission.Type.*;
65 65
66 66
67 67
...@@ -204,7 +204,7 @@ public class MastershipManager ...@@ -204,7 +204,7 @@ public class MastershipManager
204 204
205 // Create buckets reflecting current ownership. 205 // Create buckets reflecting current ownership.
206 for (ControllerNode node : nodes) { 206 for (ControllerNode node : nodes) {
207 - if (clusterService.getState(node.id()) == ACTIVE) { 207 + if (clusterService.getState(node.id()).isActive()) {
208 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id())); 208 Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
209 deviceCount += devicesOf.size(); 209 deviceCount += devicesOf.size();
210 controllerDevices.put(node, devicesOf); 210 controllerDevices.put(node, devicesOf);
......
...@@ -18,7 +18,6 @@ package org.onosproject.store.cluster.impl; ...@@ -18,7 +18,6 @@ package org.onosproject.store.cluster.impl;
18 import com.google.common.base.MoreObjects; 18 import com.google.common.base.MoreObjects;
19 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
21 -
22 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate; 23 import org.apache.felix.scr.annotations.Deactivate;
...@@ -90,6 +89,7 @@ public class DistributedClusterStore ...@@ -90,6 +89,7 @@ public class DistributedClusterStore
90 private final Map<NodeId, ControllerNode> allNodes = Maps.newConcurrentMap(); 89 private final Map<NodeId, ControllerNode> allNodes = Maps.newConcurrentMap();
91 private final Map<NodeId, State> nodeStates = Maps.newConcurrentMap(); 90 private final Map<NodeId, State> nodeStates = Maps.newConcurrentMap();
92 private final Map<NodeId, DateTime> nodeStateLastUpdatedTimes = Maps.newConcurrentMap(); 91 private final Map<NodeId, DateTime> nodeStateLastUpdatedTimes = Maps.newConcurrentMap();
92 +
93 private ScheduledExecutorService heartBeatSender = Executors.newSingleThreadScheduledExecutor( 93 private ScheduledExecutorService heartBeatSender = Executors.newSingleThreadScheduledExecutor(
94 groupedThreads("onos/cluster/membership", "heartbeat-sender")); 94 groupedThreads("onos/cluster/membership", "heartbeat-sender"));
95 private ExecutorService heartBeatMessageHandler = Executors.newSingleThreadExecutor( 95 private ExecutorService heartBeatMessageHandler = Executors.newSingleThreadExecutor(
...@@ -168,6 +168,11 @@ public class DistributedClusterStore ...@@ -168,6 +168,11 @@ public class DistributedClusterStore
168 } 168 }
169 169
170 @Override 170 @Override
171 + public void markFullyStarted(boolean started) {
172 + updateState(localNode.id(), started ? State.READY : State.ACTIVE);
173 + }
174 +
175 + @Override
171 public ControllerNode addNode(NodeId nodeId, IpAddress ip, int tcpPort) { 176 public ControllerNode addNode(NodeId nodeId, IpAddress ip, int tcpPort) {
172 ControllerNode node = new DefaultControllerNode(nodeId, ip, tcpPort); 177 ControllerNode node = new DefaultControllerNode(nodeId, ip, tcpPort);
173 addNode(node); 178 addNode(node);
...@@ -201,13 +206,14 @@ public class DistributedClusterStore ...@@ -201,13 +206,14 @@ public class DistributedClusterStore
201 .stream() 206 .stream()
202 .filter(node -> !(node.id().equals(localNode.id()))) 207 .filter(node -> !(node.id().equals(localNode.id())))
203 .collect(Collectors.toSet()); 208 .collect(Collectors.toSet());
204 - byte[] hbMessagePayload = SERIALIZER.encode(new HeartbeatMessage(localNode, peers)); 209 + State state = nodeStates.get(localNode.id());
210 + byte[] hbMessagePayload = SERIALIZER.encode(new HeartbeatMessage(localNode, state, peers));
205 peers.forEach((node) -> { 211 peers.forEach((node) -> {
206 heartbeatToPeer(hbMessagePayload, node); 212 heartbeatToPeer(hbMessagePayload, node);
207 State currentState = nodeStates.get(node.id()); 213 State currentState = nodeStates.get(node.id());
208 double phi = failureDetector.phi(node.id()); 214 double phi = failureDetector.phi(node.id());
209 if (phi >= PHI_FAILURE_THRESHOLD) { 215 if (phi >= PHI_FAILURE_THRESHOLD) {
210 - if (currentState == State.ACTIVE) { 216 + if (currentState.isActive()) {
211 updateState(node.id(), State.INACTIVE); 217 updateState(node.id(), State.INACTIVE);
212 notifyStateChange(node.id(), State.ACTIVE, State.INACTIVE); 218 notifyStateChange(node.id(), State.ACTIVE, State.INACTIVE);
213 } 219 }
...@@ -225,7 +231,7 @@ public class DistributedClusterStore ...@@ -225,7 +231,7 @@ public class DistributedClusterStore
225 231
226 private void notifyStateChange(NodeId nodeId, State oldState, State newState) { 232 private void notifyStateChange(NodeId nodeId, State oldState, State newState) {
227 ControllerNode node = allNodes.get(nodeId); 233 ControllerNode node = allNodes.get(nodeId);
228 - if (newState == State.ACTIVE) { 234 + if (newState.isActive()) {
229 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_ACTIVATED, node)); 235 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_ACTIVATED, node));
230 } else { 236 } else {
231 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_DEACTIVATED, node)); 237 notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_DEACTIVATED, node));
...@@ -246,6 +252,7 @@ public class DistributedClusterStore ...@@ -246,6 +252,7 @@ public class DistributedClusterStore
246 public void accept(Endpoint sender, byte[] message) { 252 public void accept(Endpoint sender, byte[] message) {
247 HeartbeatMessage hb = SERIALIZER.decode(message); 253 HeartbeatMessage hb = SERIALIZER.decode(message);
248 failureDetector.report(hb.source().id()); 254 failureDetector.report(hb.source().id());
255 + updateState(hb.source().id(), hb.state);
249 hb.knownPeers().forEach(node -> { 256 hb.knownPeers().forEach(node -> {
250 allNodes.put(node.id(), node); 257 allNodes.put(node.id(), node);
251 }); 258 });
...@@ -254,10 +261,12 @@ public class DistributedClusterStore ...@@ -254,10 +261,12 @@ public class DistributedClusterStore
254 261
255 private static class HeartbeatMessage { 262 private static class HeartbeatMessage {
256 private ControllerNode source; 263 private ControllerNode source;
264 + private State state;
257 private Set<ControllerNode> knownPeers; 265 private Set<ControllerNode> knownPeers;
258 266
259 - public HeartbeatMessage(ControllerNode source, Set<ControllerNode> members) { 267 + public HeartbeatMessage(ControllerNode source, State state, Set<ControllerNode> members) {
260 this.source = source; 268 this.source = source;
269 + this.state = state != null ? state : State.ACTIVE;
261 this.knownPeers = ImmutableSet.copyOf(members); 270 this.knownPeers = ImmutableSet.copyOf(members);
262 } 271 }
263 272
......
...@@ -22,7 +22,6 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -22,7 +22,6 @@ import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 22 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service; 23 import org.apache.felix.scr.annotations.Service;
24 import org.onosproject.cluster.ClusterService; 24 import org.onosproject.cluster.ClusterService;
25 -import org.onosproject.cluster.ControllerNode;
26 import org.onosproject.cluster.Leadership; 25 import org.onosproject.cluster.Leadership;
27 import org.onosproject.cluster.LeadershipEvent; 26 import org.onosproject.cluster.LeadershipEvent;
28 import org.onosproject.cluster.LeadershipEventListener; 27 import org.onosproject.cluster.LeadershipEventListener;
...@@ -30,10 +29,10 @@ import org.onosproject.cluster.LeadershipService; ...@@ -30,10 +29,10 @@ import org.onosproject.cluster.LeadershipService;
30 import org.onosproject.cluster.NodeId; 29 import org.onosproject.cluster.NodeId;
31 import org.onosproject.event.EventDeliveryService; 30 import org.onosproject.event.EventDeliveryService;
32 import org.onosproject.event.ListenerRegistry; 31 import org.onosproject.event.ListenerRegistry;
33 -import org.onosproject.net.intent.Key;
34 import org.onosproject.net.intent.IntentPartitionEvent; 32 import org.onosproject.net.intent.IntentPartitionEvent;
35 import org.onosproject.net.intent.IntentPartitionEventListener; 33 import org.onosproject.net.intent.IntentPartitionEventListener;
36 import org.onosproject.net.intent.IntentPartitionService; 34 import org.onosproject.net.intent.IntentPartitionService;
35 +import org.onosproject.net.intent.Key;
37 import org.slf4j.Logger; 36 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory; 37 import org.slf4j.LoggerFactory;
39 38
...@@ -173,7 +172,7 @@ public class IntentPartitionManager implements IntentPartitionService { ...@@ -173,7 +172,7 @@ public class IntentPartitionManager implements IntentPartitionService {
173 private void rebalance() { 172 private void rebalance() {
174 int activeNodes = (int) clusterService.getNodes() 173 int activeNodes = (int) clusterService.getNodes()
175 .stream() 174 .stream()
176 - .filter(node -> ControllerNode.State.ACTIVE == clusterService.getState(node.id())) 175 + .filter(node -> clusterService.getState(node.id()).isActive())
177 .count(); 176 .count();
178 177
179 int myShare = (int) Math.ceil((double) NUM_PARTITIONS / activeNodes); 178 int myShare = (int) Math.ceil((double) NUM_PARTITIONS / activeNodes);
......
...@@ -556,7 +556,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -556,7 +556,7 @@ public class EventuallyConsistentMapImpl<K, V>
556 .stream() 556 .stream()
557 .map(ControllerNode::id) 557 .map(ControllerNode::id)
558 .filter(id -> !localNodeId.equals(id)) 558 .filter(id -> !localNodeId.equals(id))
559 - .filter(id -> clusterService.getState(id) == ControllerNode.State.ACTIVE) 559 + .filter(id -> clusterService.getState(id).isActive())
560 .collect(Collectors.toList()); 560 .collect(Collectors.toList());
561 Collections.shuffle(activePeers); 561 Collections.shuffle(activePeers);
562 return activePeers.isEmpty() ? Optional.empty() : Optional.of(activePeers.get(0)); 562 return activePeers.isEmpty() ? Optional.empty() : Optional.of(activePeers.get(0));
......
...@@ -200,7 +200,7 @@ public class MutexExecutionManager implements MutexExecutionService { ...@@ -200,7 +200,7 @@ public class MutexExecutionManager implements MutexExecutionService {
200 long activeNodes = clusterService.getNodes() 200 long activeNodes = clusterService.getNodes()
201 .stream() 201 .stream()
202 .map(node -> clusterService.getState(node.id())) 202 .map(node -> clusterService.getState(node.id()))
203 - .filter(State.ACTIVE::equals) 203 + .filter(State::isActive)
204 .count(); 204 .count();
205 if (clusterService.getNodes().size() > 1 && activeNodes == 1) { 205 if (clusterService.getNodes().size() > 1 && activeNodes == 1) {
206 log.info("This node is partitioned away from the cluster. Stopping all inflight executions"); 206 log.info("This node is partitioned away from the cluster. Stopping all inflight executions");
......
...@@ -262,6 +262,12 @@ ...@@ -262,6 +262,12 @@
262 <version>1.9.12</version> 262 <version>1.9.12</version>
263 <scope>provided</scope> 263 <scope>provided</scope>
264 </dependency> 264 </dependency>
265 + <dependency>
266 + <groupId>org.apache.felix</groupId>
267 + <artifactId>org.apache.felix.scr</artifactId>
268 + <version>1.8.2</version>
269 + </dependency>
270 +
265 271
266 <dependency> 272 <dependency>
267 <groupId>org.apache.karaf.features</groupId> 273 <groupId>org.apache.karaf.features</groupId>
......
...@@ -44,10 +44,11 @@ public class ClusterViewMessageHandler extends UiMessageHandler { ...@@ -44,10 +44,11 @@ public class ClusterViewMessageHandler extends UiMessageHandler {
44 private static final String IP = "ip"; 44 private static final String IP = "ip";
45 private static final String TCP_PORT = "tcp"; 45 private static final String TCP_PORT = "tcp";
46 private static final String STATE_IID = "_iconid_state"; 46 private static final String STATE_IID = "_iconid_state";
47 + private static final String STARTED_IID = "_iconid_started";
47 private static final String UPDATED = "updated"; 48 private static final String UPDATED = "updated";
48 49
49 private static final String[] COL_IDS = { 50 private static final String[] COL_IDS = {
50 - ID, IP, TCP_PORT, STATE_IID, UPDATED 51 + ID, IP, TCP_PORT, STATE_IID, STARTED_IID, UPDATED
51 }; 52 };
52 53
53 private static final String ICON_ID_ONLINE = "active"; 54 private static final String ICON_ID_ONLINE = "active";
...@@ -95,13 +96,15 @@ public class ClusterViewMessageHandler extends UiMessageHandler { ...@@ -95,13 +96,15 @@ public class ClusterViewMessageHandler extends UiMessageHandler {
95 ClusterService cs) { 96 ClusterService cs) {
96 NodeId id = node.id(); 97 NodeId id = node.id();
97 DateTime lastUpdated = cs.getLastUpdated(id); 98 DateTime lastUpdated = cs.getLastUpdated(id);
98 - String iconId = (cs.getState(id) == ControllerNode.State.ACTIVE) ? 99 + ControllerNode.State state = cs.getState(id);
99 - ICON_ID_ONLINE : ICON_ID_OFFLINE; 100 + String iconId = state.isActive() ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
101 + String startedId = state.isReady() ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
100 102
101 row.cell(ID, id) 103 row.cell(ID, id)
102 .cell(IP, node.ip()) 104 .cell(IP, node.ip())
103 .cell(TCP_PORT, node.tcpPort()) 105 .cell(TCP_PORT, node.tcpPort())
104 .cell(STATE_IID, iconId) 106 .cell(STATE_IID, iconId)
107 + .cell(STARTED_IID, startedId)
105 .cell(UPDATED, lastUpdated); 108 .cell(UPDATED, lastUpdated);
106 } 109 }
107 } 110 }
......
...@@ -81,7 +81,6 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -81,7 +81,6 @@ import java.util.concurrent.ConcurrentHashMap;
81 81
82 import static com.google.common.base.Preconditions.checkNotNull; 82 import static com.google.common.base.Preconditions.checkNotNull;
83 import static com.google.common.base.Strings.isNullOrEmpty; 83 import static com.google.common.base.Strings.isNullOrEmpty;
84 -import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
85 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; 84 import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
86 import static org.onosproject.net.PortNumber.portNumber; 85 import static org.onosproject.net.PortNumber.portNumber;
87 import static org.onosproject.ui.topo.TopoConstants.CoreButtons; 86 import static org.onosproject.ui.topo.TopoConstants.CoreButtons;
...@@ -230,7 +229,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { ...@@ -230,7 +229,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
230 ObjectNode payload = objectNode() 229 ObjectNode payload = objectNode()
231 .put("id", node.id().toString()) 230 .put("id", node.id().toString())
232 .put("ip", node.ip().toString()) 231 .put("ip", node.ip().toString())
233 - .put("online", clusterService.getState(node.id()) == ACTIVE) 232 + .put("online", clusterService.getState(node.id()).isActive())
234 .put("uiAttached", node.equals(clusterService.getLocalNode())) 233 .put("uiAttached", node.equals(clusterService.getLocalNode()))
235 .put("switches", switchCount); 234 .put("switches", switchCount);
236 235
......
...@@ -30,7 +30,8 @@ ...@@ -30,7 +30,8 @@
30 <div class="table-header" onos-sortable-header> 30 <div class="table-header" onos-sortable-header>
31 <table> 31 <table>
32 <tr> 32 <tr>
33 - <td colId="_iconid_state" class="table-icon" sortable></td> 33 + <td colId="_iconid_state" class="table-icon" col-width="60px" sortable>Active </td>
34 + <td colId="_iconid_started" class="table-icon" col-width="60px" sortable>Started </td>
34 <td colId="id" sortable>ID </td> 35 <td colId="id" sortable>ID </td>
35 <td colId="ip" sortable>IP Address </td> 36 <td colId="ip" sortable>IP Address </td>
36 <td colId="tcp" sortable>TCP Port </td> 37 <td colId="tcp" sortable>TCP Port </td>
...@@ -52,6 +53,9 @@ ...@@ -52,6 +53,9 @@
52 <td class="table-icon"> 53 <td class="table-icon">
53 <div icon icon-id="{{node._iconid_state}}"></div> 54 <div icon icon-id="{{node._iconid_state}}"></div>
54 </td> 55 </td>
56 + <td class="table-icon">
57 + <div icon icon-id="{{node._iconid_started}}"></div>
58 + </td>
55 <td>{{node.id}}</td> 59 <td>{{node.id}}</td>
56 <td>{{node.ip}}</td> 60 <td>{{node.ip}}</td>
57 <td>{{node.tcp}}</td> 61 <td>{{node.tcp}}</td>
......