blob: a3e7bd41be36e7f627058f678d1370a90562d919 [file] [log] [blame]
Hyunsun Mooncb799442016-01-15 20:03:18 -08001/*
Brian O'Connor8e57fd52016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Hyunsun Mooncb799442016-01-15 20:03:18 -08003 *
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 */
Hyunsun Moonf9480202016-04-14 16:13:42 -070016package org.onosproject.cordvtn.impl;
Hyunsun Mooncb799442016-01-15 20:03:18 -080017
18import com.google.common.collect.Sets;
Hyunsun Moon126171d2016-02-09 01:55:48 -080019import com.jcraft.jsch.Session;
Hyunsun Mooncb799442016-01-15 20:03:18 -080020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Hyunsun Moon126171d2016-02-09 01:55:48 -080026import org.onlab.packet.IpAddress;
Hyunsun Mooncb799442016-01-15 20:03:18 -080027import org.onlab.util.ItemNotFoundException;
28import org.onlab.util.KryoNamespace;
29import org.onosproject.cluster.ClusterService;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -080030import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
Hyunsun Moonf9480202016-04-14 16:13:42 -070032import org.onosproject.cordvtn.api.ConnectionHandler;
33import org.onosproject.cordvtn.api.CordVtnConfig;
34import org.onosproject.cordvtn.api.CordVtnNode;
35import org.onosproject.cordvtn.api.CordVtnNodeState;
36import org.onosproject.cordvtn.api.CordVtnService;
37import org.onosproject.cordvtn.api.NetworkAddress;
38import org.onosproject.cordvtn.api.SshAccessInfo;
Hyunsun Mooncb799442016-01-15 20:03:18 -080039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080041import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.DefaultAnnotations;
43import org.onosproject.net.Device;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Host;
46import org.onosproject.net.Port;
47import org.onosproject.net.behaviour.BridgeConfig;
48import org.onosproject.net.behaviour.BridgeName;
49import org.onosproject.net.behaviour.ControllerInfo;
50import org.onosproject.net.behaviour.DefaultTunnelDescription;
51import org.onosproject.net.behaviour.TunnelConfig;
52import org.onosproject.net.behaviour.TunnelDescription;
53import org.onosproject.net.behaviour.TunnelName;
Hyunsun Mooncb799442016-01-15 20:03:18 -080054import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
57import org.onosproject.net.config.NetworkConfigService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080058import org.onosproject.net.device.DeviceAdminService;
59import org.onosproject.net.device.DeviceEvent;
60import org.onosproject.net.device.DeviceListener;
61import org.onosproject.net.device.DeviceService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080062import org.onosproject.net.flow.FlowRuleService;
63import org.onosproject.net.group.GroupService;
64import org.onosproject.net.host.HostService;
65import org.onosproject.ovsdb.controller.OvsdbClientService;
66import org.onosproject.ovsdb.controller.OvsdbController;
67import org.onosproject.ovsdb.controller.OvsdbNodeId;
68import org.onosproject.store.serializers.KryoNamespaces;
69import org.onosproject.store.service.ConsistentMap;
Hyunsun Moonff55e812016-03-10 12:40:16 -080070import org.onosproject.store.service.MapEvent;
71import org.onosproject.store.service.MapEventListener;
Hyunsun Mooncb799442016-01-15 20:03:18 -080072import org.onosproject.store.service.Serializer;
73import org.onosproject.store.service.StorageService;
Hyunsun Moond05b32e2016-03-02 19:27:26 -080074import org.onosproject.store.service.Versioned;
Hyunsun Mooncb799442016-01-15 20:03:18 -080075import org.slf4j.Logger;
76
77import java.util.ArrayList;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -080081import java.util.Objects;
Hyunsun Moon126171d2016-02-09 01:55:48 -080082import java.util.Set;
Hyunsun Mooncb799442016-01-15 20:03:18 -080083import java.util.concurrent.ExecutorService;
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -080084import java.util.stream.Collectors;
Hyunsun Mooncb799442016-01-15 20:03:18 -080085
86import static com.google.common.base.Preconditions.checkNotNull;
87import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
88import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon479b7752016-05-06 20:13:28 -070089import static org.onosproject.cordvtn.impl.RemoteIpCommandUtil.*;
Hyunsun Mooncb799442016-01-15 20:03:18 -080090import static org.onosproject.net.Device.Type.SWITCH;
91import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
92import static org.slf4j.LoggerFactory.getLogger;
93
94/**
95 * Reads node information from the network config file and handles the config
96 * update events.
97 * Only a leader controller performs the node addition or deletion.
98 */
99@Component(immediate = true)
100@Service(value = CordVtnNodeManager.class)
101public class CordVtnNodeManager {
102
103 protected final Logger log = getLogger(getClass());
104
105 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
106 .register(KryoNamespaces.API)
107 .register(CordVtnNode.class)
Hyunsun Moon126171d2016-02-09 01:55:48 -0800108 .register(NodeState.class)
109 .register(SshAccessInfo.class)
110 .register(NetworkAddress.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800111
112 private static final String DEFAULT_BRIDGE = "br-int";
113 private static final String DEFAULT_TUNNEL = "vxlan";
114 private static final String VPORT_PREFIX = "tap";
115 private static final String OK = "OK";
116 private static final String NO = "NO";
117
118 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
119 {
120 put("key", "flow");
121 put("remote_ip", "flow");
122 }
123 };
124 private static final int DPID_BEGIN = 3;
125 private static final int OFPORT = 6653;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected CoreService coreService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected NetworkConfigRegistry configRegistry;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected NetworkConfigService configService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected StorageService storageService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected DeviceAdminService adminService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected OvsdbController controller;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected ClusterService clusterService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Mooncb799442016-01-15 20:03:18 -0800149 protected DeviceService deviceService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected HostService hostService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected FlowRuleService flowRuleService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800158 protected LeadershipService leadershipService;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected GroupService groupService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected CordVtnService cordVtnService;
165
Hyunsun Mooncb799442016-01-15 20:03:18 -0800166 private final ExecutorService eventExecutor =
167 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler"));
168
169 private final NetworkConfigListener configListener = new InternalConfigListener();
170 private final DeviceListener deviceListener = new InternalDeviceListener();
Hyunsun Moonff55e812016-03-10 12:40:16 -0800171 private final MapEventListener<String, CordVtnNode> nodeStoreListener = new InternalMapListener();
Hyunsun Mooncb799442016-01-15 20:03:18 -0800172
173 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
174 private final BridgeHandler bridgeHandler = new BridgeHandler();
175
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800176 private ConsistentMap<String, CordVtnNode> nodeStore;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800177 private CordVtnRuleInstaller ruleInstaller;
178 private ApplicationId appId;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800179 private NodeId localNodeId;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800180
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800181 private enum NodeState implements CordVtnNodeState {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800182
183 INIT {
184 @Override
185 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800186 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800187 nodeManager.connectOvsdb(node);
188 } else {
189 nodeManager.createIntegrationBridge(node);
190 }
191 }
192 },
193 BRIDGE_CREATED {
194 @Override
195 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800196 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800197 nodeManager.connectOvsdb(node);
198 } else {
199 nodeManager.createTunnelInterface(node);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800200 nodeManager.addDataPlaneInterface(node);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800201 }
202 }
203 },
Hyunsun Moon126171d2016-02-09 01:55:48 -0800204 PORTS_ADDED {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800205 @Override
206 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800207 nodeManager.setIpAddress(node);
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800208 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800209 },
Hyunsun Mooncb799442016-01-15 20:03:18 -0800210 COMPLETE {
211 @Override
212 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
213 nodeManager.postInit(node);
214 }
215 },
216 INCOMPLETE {
217 @Override
218 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
219 }
220 };
221
Hyunsun Mooncb799442016-01-15 20:03:18 -0800222 public abstract void process(CordVtnNodeManager nodeManager, CordVtnNode node);
223 }
224
225 @Activate
Hyunsun Moon479b7752016-05-06 20:13:28 -0700226 protected void activate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800227 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800228 localNodeId = clusterService.getLocalNode().id();
229 leadershipService.runForLeadership(appId.name());
230
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800231 nodeStore = storageService.<String, CordVtnNode>consistentMapBuilder()
Hyunsun Mooncb799442016-01-15 20:03:18 -0800232 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
233 .withName("cordvtn-nodestore")
234 .withApplicationId(appId)
235 .build();
236
237 ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
238 deviceService,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800239 groupService,
Hyunsun Moon486ed1b2016-05-13 18:58:35 -0700240 hostService,
Hyunsun Moon7a9bb122016-03-08 18:07:52 -0800241 configRegistry,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800242 DEFAULT_TUNNEL);
243
Hyunsun Moonff55e812016-03-10 12:40:16 -0800244 nodeStore.addListener(nodeStoreListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800245 deviceService.addListener(deviceListener);
246 configService.addListener(configListener);
Hyunsun Moon479b7752016-05-06 20:13:28 -0700247
248 log.info("Started");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800249 }
250
251 @Deactivate
252 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800253 configService.removeListener(configListener);
254 deviceService.removeListener(deviceListener);
Hyunsun Moonff55e812016-03-10 12:40:16 -0800255 nodeStore.removeListener(nodeStoreListener);
Hyunsun Moonff55e812016-03-10 12:40:16 -0800256
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800257 leadershipService.withdraw(appId.name());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800258 eventExecutor.shutdown();
Hyunsun Moon479b7752016-05-06 20:13:28 -0700259
260 log.info("Stopped");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800261 }
262
263 /**
Hyunsun Moonff55e812016-03-10 12:40:16 -0800264 * Adds or updates a new node to the service.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800265 *
266 * @param node cordvtn node
267 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800268 public void addOrUpdateNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800269 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800270 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800271 }
272
273 /**
274 * Deletes a node from the service.
275 *
276 * @param node cordvtn node
277 */
278 public void deleteNode(CordVtnNode node) {
279 checkNotNull(node);
280
Hyunsun Moon126171d2016-02-09 01:55:48 -0800281 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800282 disconnectOvsdb(node);
283 }
284
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800285 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800286 }
287
288 /**
289 * Initiates node to serve virtual tenant network.
290 *
291 * @param node cordvtn node
292 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800293 private void initNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800294 checkNotNull(node);
295
Hyunsun Moonff55e812016-03-10 12:40:16 -0800296 NodeState state = (NodeState) node.state();
297 log.debug("Processing node: {} state: {}", node.hostname(), state);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800298
Hyunsun Mooncb799442016-01-15 20:03:18 -0800299 state.process(this, node);
300 }
301
302 /**
303 * Returns node initialization state.
304 *
305 * @param node cordvtn node
306 * @return true if initial node setup is completed, otherwise false
307 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800308 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800309 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800310 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800311 }
312
313 /**
Hyunsun Mooncbc885f2016-03-08 15:37:57 -0800314 * Flush flows installed by cordvtn.
315 */
316 public void flushRules() {
317 ruleInstaller.flushRules();
318 }
319
320 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800321 * Returns if current node state saved in nodeStore is COMPLETE or not.
322 *
323 * @param node cordvtn node
324 * @return true if it's complete state, otherwise false
325 */
326 private boolean isNodeStateComplete(CordVtnNode node) {
327 checkNotNull(node);
328
329 // the state saved in nodeStore can be wrong if IP address settings are changed
330 // after the node init has been completed since there's no way to detect it
331 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800332 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
333 CordVtnNodeState state = versionedNode.value().state();
334 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800335 }
336
337 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800338 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800339 *
340 * @param node cordvtn node
341 * @return string including detailed node init state
342 */
343 public String checkNodeInitState(CordVtnNode node) {
344 checkNotNull(node);
345
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800346 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800347 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800348 return null;
349 }
350
Hyunsun Moon479b7752016-05-06 20:13:28 -0700351 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800352 if (session == null) {
353 log.debug("Failed to SSH to {}", node.hostname());
354 return null;
355 }
356
Hyunsun Moon479b7752016-05-06 20:13:28 -0700357 Set<IpAddress> intBrIps = getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800358 String result = String.format(
Hyunsun Moon479b7752016-05-06 20:13:28 -0700359 "Current state : %s%n" +
360 "br-int created and connected to ONOS : %s (%s)%n" +
361 "VXLAN interface added to br-int : %s%n" +
362 "Data plane interface is added to br-int and enabled : %s (%s)%n" +
363 "IP flushed from data plane interface : %s (%s)%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800364 "Data plane IP added to br-int : %s (%s)%n" +
365 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon479b7752016-05-06 20:13:28 -0700366 node.state(),
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800367 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800368 isTunnelIntfCreated(node) ? OK : NO,
369 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
Hyunsun Moon479b7752016-05-06 20:13:28 -0700370 isInterfaceUp(session, node.dpIntf()) &&
371 getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO, node.dpIntf(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800372 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
373 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
374
Hyunsun Moon479b7752016-05-06 20:13:28 -0700375 disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800376
377 return result;
378 }
379
380 /**
381 * Returns the number of the nodes known to the service.
382 *
383 * @return number of nodes
384 */
385 public int getNodeCount() {
386 return nodeStore.size();
387 }
388
389 /**
390 * Returns all nodes known to the service.
391 *
392 * @return list of nodes
393 */
394 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800395 return nodeStore.values().stream()
396 .map(Versioned::value)
397 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800398 }
399
400 /**
401 * Returns cordvtn node associated with a given OVSDB device.
402 *
403 * @param ovsdbId OVSDB device id
404 * @return cordvtn node, null if it fails to find the node
405 */
406 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
407 return getNodes().stream()
408 .filter(node -> node.ovsdbId().equals(ovsdbId))
409 .findFirst().orElse(null);
410 }
411
412 /**
413 * Returns cordvtn node associated with a given integration bridge.
414 *
415 * @param bridgeId device id of integration bridge
416 * @return cordvtn node, null if it fails to find the node
417 */
418 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
419 return getNodes().stream()
420 .filter(node -> node.intBrId().equals(bridgeId))
421 .findFirst().orElse(null);
422 }
423
424 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800425 * Sets a new state for a given cordvtn node.
426 *
427 * @param node cordvtn node
428 * @param newState new node state
429 */
430 private void setNodeState(CordVtnNode node, NodeState newState) {
431 checkNotNull(node);
432
Hyunsun Moonff55e812016-03-10 12:40:16 -0800433 log.debug("Changed {} state: {}", node.hostname(), newState);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800434 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800435 }
436
437 /**
438 * Checks current state of a given cordvtn node and returns it.
439 *
440 * @param node cordvtn node
441 * @return node state
442 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800443 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800444 checkNotNull(node);
445
Hyunsun Moon126171d2016-02-09 01:55:48 -0800446 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
447 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800448 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800449 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
450 return NodeState.PORTS_ADDED;
451 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800452 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800453 } else {
454 return NodeState.INIT;
455 }
456 }
457
458 /**
459 * Performs tasks after node initialization.
460 * It disconnects unnecessary OVSDB connection and installs initial flow
461 * rules on the device.
462 *
463 * @param node cordvtn node
464 */
465 private void postInit(CordVtnNode node) {
466 disconnectOvsdb(node);
467
Hyunsun Moon126171d2016-02-09 01:55:48 -0800468 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800469
470 // add existing hosts to the service
471 deviceService.getPorts(node.intBrId()).stream()
472 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
473 port.isEnabled())
474 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
475
476 // remove stale hosts from the service
477 hostService.getHosts().forEach(host -> {
478 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
479 if (port == null) {
480 cordVtnService.removeServiceVm(getConnectPoint(host));
481 }
482 });
483
484 log.info("Finished init {}", node.hostname());
485 }
486
487 /**
488 * Returns port name.
489 *
490 * @param port port
491 * @return port name
492 */
493 private String getPortName(Port port) {
494 return port.annotations().value("portName");
495 }
496
497 /**
498 * Returns connection state of OVSDB server for a given node.
499 *
500 * @param node cordvtn node
501 * @return true if it is connected, false otherwise
502 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800503 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800504 checkNotNull(node);
505
506 OvsdbClientService ovsdbClient = getOvsdbClient(node);
507 return deviceService.isAvailable(node.ovsdbId()) &&
508 ovsdbClient != null && ovsdbClient.isConnected();
509 }
510
511 /**
512 * Connects to OVSDB server for a given node.
513 *
514 * @param node cordvtn node
515 */
516 private void connectOvsdb(CordVtnNode node) {
517 checkNotNull(node);
518
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800519 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800520 log.warn("Node {} does not exist", node.hostname());
521 return;
522 }
523
Hyunsun Moon126171d2016-02-09 01:55:48 -0800524 if (!isOvsdbConnected(node)) {
525 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800526 }
527 }
528
529 /**
530 * Disconnects OVSDB server for a given node.
531 *
532 * @param node cordvtn node
533 */
534 private void disconnectOvsdb(CordVtnNode node) {
535 checkNotNull(node);
536
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800537 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800538 log.warn("Node {} does not exist", node.hostname());
539 return;
540 }
541
Hyunsun Moon126171d2016-02-09 01:55:48 -0800542 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800543 OvsdbClientService ovsdbClient = getOvsdbClient(node);
544 ovsdbClient.disconnect();
545 }
546 }
547
548 /**
549 * Returns OVSDB client for a given node.
550 *
551 * @param node cordvtn node
552 * @return OVSDB client, or null if it fails to get OVSDB client
553 */
554 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
555 checkNotNull(node);
556
557 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800558 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800559 if (ovsdbClient == null) {
560 log.trace("Couldn't find OVSDB client for {}", node.hostname());
561 }
562 return ovsdbClient;
563 }
564
565 /**
566 * Creates an integration bridge for a given node.
567 *
568 * @param node cordvtn node
569 */
570 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800571 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800572 return;
573 }
574
575 List<ControllerInfo> controllers = new ArrayList<>();
576 Sets.newHashSet(clusterService.getNodes()).stream()
577 .forEach(controller -> {
578 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
579 controllers.add(ctrlInfo);
580 });
581
582 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
583
584 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700585 Device device = deviceService.getDevice(node.ovsdbId());
586 if (device.is(BridgeConfig.class)) {
587 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
588 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
589 } else {
590 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
591 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800592 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800593 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800594 }
595 }
596
597 /**
598 * Creates tunnel interface to the integration bridge for a given node.
599 *
600 * @param node cordvtn node
601 */
602 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800603 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800604 return;
605 }
606
607 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
608 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
609 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
610 }
611
612 TunnelDescription description = new DefaultTunnelDescription(
613 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
614 optionBuilder.build());
615
616 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700617 Device device = deviceService.getDevice(node.ovsdbId());
618 if (device.is(TunnelConfig.class)) {
619 TunnelConfig tunnelConfig = device.as(TunnelConfig.class);
620 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
621 } else {
622 log.warn("The tunneling behaviour is not supported in device {}", device.id().toString());
623 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800624 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800625 log.warn("Failed to create tunnel interface on {}", node.hostname());
626 }
627 }
628
629 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800630 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800631 *
632 * @param node cordvtn node
633 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800634 private void addDataPlaneInterface(CordVtnNode node) {
635 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800636 return;
637 }
638
Hyunsun Moon479b7752016-05-06 20:13:28 -0700639 Session session = connect(node.sshInfo());
640 if (session == null) {
641 log.debug("Failed to SSH to {}", node.hostname());
642 return;
643 }
644
645 if (!isInterfaceUp(session, node.dpIntf())) {
646 log.warn("Interface {} is not available", node.dpIntf());
647 return;
648 }
649 disconnect(session);
650
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800651 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700652 Device device = deviceService.getDevice(node.ovsdbId());
653 if (device.is(BridgeConfig.class)) {
654 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
655 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
656 } else {
657 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
658 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800659 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800660 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
661 }
662 }
663
664 /**
665 * Flushes IP address from data plane interface and adds data plane IP address
666 * to integration bridge.
667 *
668 * @param node cordvtn node
669 */
670 private void setIpAddress(CordVtnNode node) {
Hyunsun Moon479b7752016-05-06 20:13:28 -0700671 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800672 if (session == null) {
673 log.debug("Failed to SSH to {}", node.hostname());
674 return;
675 }
676
Hyunsun Moon479b7752016-05-06 20:13:28 -0700677 getCurrentIps(session, DEFAULT_BRIDGE).stream()
Hyunsun Moonf2760522016-03-04 19:24:08 -0800678 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
679 .filter(ip -> !ip.equals(node.dpIp().ip()))
Hyunsun Moon479b7752016-05-06 20:13:28 -0700680 .forEach(ip -> deleteIp(session, ip, DEFAULT_BRIDGE));
Hyunsun Moonf2760522016-03-04 19:24:08 -0800681
Hyunsun Moon479b7752016-05-06 20:13:28 -0700682 boolean result = flushIp(session, node.dpIntf()) &&
683 setInterfaceUp(session, node.dpIntf()) &&
684 addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
685 addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
686 setInterfaceUp(session, DEFAULT_BRIDGE);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800687
Hyunsun Moon479b7752016-05-06 20:13:28 -0700688 disconnect(session);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800689
690 if (result) {
691 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800692 }
693 }
694
695 /**
696 * Checks if integration bridge exists and available.
697 *
698 * @param node cordvtn node
699 * @return true if the bridge is available, false otherwise
700 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800701 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800702 return (deviceService.getDevice(node.intBrId()) != null
703 && deviceService.isAvailable(node.intBrId()));
704 }
705
706 /**
707 * Checks if tunnel interface exists.
708 *
709 * @param node cordvtn node
710 * @return true if the interface exists, false otherwise
711 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800712 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800713 return deviceService.getPorts(node.intBrId())
714 .stream()
715 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
716 p.isEnabled())
717 .findAny().isPresent();
718 }
719
720 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800721 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800722 *
723 * @param node cordvtn node
724 * @return true if the interface exists, false otherwise
725 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800726 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800727 return deviceService.getPorts(node.intBrId())
728 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800729 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800730 p.isEnabled())
731 .findAny().isPresent();
732 }
733
734 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800735 * Checks if the IP addresses are correctly set.
736 *
737 * @param node cordvtn node
738 * @return true if the IP is set, false otherwise
739 */
740 private boolean isIpAddressSet(CordVtnNode node) {
Hyunsun Moon479b7752016-05-06 20:13:28 -0700741 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800742 if (session == null) {
743 log.debug("Failed to SSH to {}", node.hostname());
744 return false;
745 }
746
Hyunsun Moon479b7752016-05-06 20:13:28 -0700747 Set<IpAddress> intBrIps = getCurrentIps(session, DEFAULT_BRIDGE);
748 boolean result = getCurrentIps(session, node.dpIntf()).isEmpty() &&
749 isInterfaceUp(session, node.dpIntf()) &&
Hyunsun Moon126171d2016-02-09 01:55:48 -0800750 intBrIps.contains(node.dpIp().ip()) &&
751 intBrIps.contains(node.localMgmtIp().ip()) &&
Hyunsun Moon479b7752016-05-06 20:13:28 -0700752 isInterfaceUp(session, DEFAULT_BRIDGE);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800753
Hyunsun Moon479b7752016-05-06 20:13:28 -0700754 disconnect(session);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800755 return result;
756 }
757
758 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800759 * Returns connect point of a given port.
760 *
761 * @param port port
762 * @return connect point
763 */
764 private ConnectPoint getConnectPoint(Port port) {
765 return new ConnectPoint(port.element().id(), port.number());
766 }
767
768 /**
769 * Returns connect point of a given host.
770 *
771 * @param host host
772 * @return connect point
773 */
774 private ConnectPoint getConnectPoint(Host host) {
775 return new ConnectPoint(host.location().deviceId(), host.location().port());
776 }
777
778 private class OvsdbHandler implements ConnectionHandler<Device> {
779
780 @Override
781 public void connected(Device device) {
782 CordVtnNode node = getNodeByOvsdbId(device.id());
783 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800784 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800785 } else {
786 log.debug("{} is detected on unregistered node, ignore it.", device.id());
787 }
788 }
789
790 @Override
791 public void disconnected(Device device) {
792 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800793 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800794 adminService.removeDevice(device.id());
795 }
796 }
797 }
798
799 private class BridgeHandler implements ConnectionHandler<Device> {
800
801 @Override
802 public void connected(Device device) {
803 CordVtnNode node = getNodeByBridgeId(device.id());
804 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800805 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800806 } else {
807 log.debug("{} is detected on unregistered node, ignore it.", device.id());
808 }
809 }
810
811 @Override
812 public void disconnected(Device device) {
813 CordVtnNode node = getNodeByBridgeId(device.id());
814 if (node != null) {
815 log.debug("Integration Bridge is disconnected from {}", node.hostname());
816 setNodeState(node, NodeState.INCOMPLETE);
817 }
818 }
819
820 /**
821 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800822 * If the added port is tunnel or data plane interface, proceed to the remaining
823 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800824 *
825 * @param port port
826 */
827 public void portAdded(Port port) {
828 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
829 String portName = getPortName(port);
830
831 if (node == null) {
832 log.debug("{} is added to unregistered node, ignore it.", portName);
833 return;
834 }
835
Hyunsun Moonff55e812016-03-10 12:40:16 -0800836 log.info("Port {} is added to {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800837
838 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800839 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800840 cordVtnService.addServiceVm(node, getConnectPoint(port));
841 } else {
842 log.debug("VM is detected on incomplete node, ignore it.", portName);
843 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800844 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
845 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800846 }
847 }
848
849 /**
850 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800851 * If the removed port is tunnel or data plane interface, proceed to the remaining
852 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800853 *
854 * @param port port
855 */
856 public void portRemoved(Port port) {
857 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
858 String portName = getPortName(port);
859
860 if (node == null) {
861 return;
862 }
863
Hyunsun Moonff55e812016-03-10 12:40:16 -0800864 log.info("Port {} is removed from {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800865
866 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800867 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800868 cordVtnService.removeServiceVm(getConnectPoint(port));
869 } else {
870 log.debug("VM is vanished from incomplete node, ignore it.", portName);
871 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800872 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800873 setNodeState(node, NodeState.INCOMPLETE);
874 }
875 }
876 }
877
878 private class InternalDeviceListener implements DeviceListener {
879
880 @Override
881 public void event(DeviceEvent event) {
882
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800883 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
884 if (!Objects.equals(localNodeId, leaderNodeId)) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800885 // do not allow to proceed without leadership
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800886 return;
887 }
888
Hyunsun Mooncb799442016-01-15 20:03:18 -0800889 Device device = event.subject();
890 ConnectionHandler<Device> handler =
891 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
892
893 switch (event.type()) {
894 case PORT_ADDED:
Hyunsun Moonff55e812016-03-10 12:40:16 -0800895 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800896 break;
897 case PORT_UPDATED:
898 if (!event.port().isEnabled()) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800899 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800900 }
901 break;
902 case DEVICE_ADDED:
903 case DEVICE_AVAILABILITY_CHANGED:
904 if (deviceService.isAvailable(device.id())) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800905 eventExecutor.execute(() -> handler.connected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800906 } else {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800907 eventExecutor.execute(() -> handler.disconnected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800908 }
909 break;
910 default:
911 break;
912 }
913 }
914 }
915
916 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800917 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800918 */
919 private void readConfiguration() {
920 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800921 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800922 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800923 return;
924 }
925
Hyunsun Moonff55e812016-03-10 12:40:16 -0800926 config.cordVtnNodes().forEach(this::addOrUpdateNode);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800927 }
928
929 private class InternalConfigListener implements NetworkConfigListener {
930
931 @Override
932 public void event(NetworkConfigEvent event) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800933 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
934 if (!Objects.equals(localNodeId, leaderNodeId)) {
935 // do not allow to proceed without leadership
936 return;
937 }
938
Hyunsun Mooncb799442016-01-15 20:03:18 -0800939 if (!event.configClass().equals(CordVtnConfig.class)) {
940 return;
941 }
942
943 switch (event.type()) {
944 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800945 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800946 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
947 break;
948 default:
949 break;
950 }
951 }
952 }
Hyunsun Moonff55e812016-03-10 12:40:16 -0800953
954 private class InternalMapListener implements MapEventListener<String, CordVtnNode> {
955
956 @Override
957 public void event(MapEvent<String, CordVtnNode> event) {
958 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
959 if (!Objects.equals(localNodeId, leaderNodeId)) {
960 // do not allow to proceed without leadership
961 return;
962 }
963
964 CordVtnNode oldNode;
965 CordVtnNode newNode;
966
967 switch (event.type()) {
968 case UPDATE:
969 oldNode = event.oldValue().value();
970 newNode = event.newValue().value();
971
Hyunsun Moon479b7752016-05-06 20:13:28 -0700972 log.info("Reloaded {}", newNode.hostname());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800973 if (!newNode.equals(oldNode)) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800974 log.debug("New node: {}", newNode);
975 }
Hyunsun Moon479b7752016-05-06 20:13:28 -0700976 // performs init procedure even if the node is not changed
977 // for robustness since it's no harm to run init procedure
978 // multiple times
Hyunsun Moonff55e812016-03-10 12:40:16 -0800979 eventExecutor.execute(() -> initNode(newNode));
980 break;
981 case INSERT:
982 newNode = event.newValue().value();
983 log.info("Added {}", newNode.hostname());
984 eventExecutor.execute(() -> initNode(newNode));
985 break;
986 case REMOVE:
987 oldNode = event.oldValue().value();
Hyunsun Moon479b7752016-05-06 20:13:28 -0700988 log.info("Removed {}", oldNode.hostname());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800989 break;
990 default:
991 break;
992 }
993 }
994 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800995}