blob: acdd0180bbf0b01c5bbb85f081e5d3af1083af5c [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 Moon7a9bb122016-03-08 18:07:52 -0800240 configRegistry,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800241 DEFAULT_TUNNEL);
242
Hyunsun Moonff55e812016-03-10 12:40:16 -0800243 nodeStore.addListener(nodeStoreListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800244 deviceService.addListener(deviceListener);
245 configService.addListener(configListener);
Hyunsun Moon479b7752016-05-06 20:13:28 -0700246
247 log.info("Started");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800248 }
249
250 @Deactivate
251 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800252 configService.removeListener(configListener);
253 deviceService.removeListener(deviceListener);
Hyunsun Moonff55e812016-03-10 12:40:16 -0800254 nodeStore.removeListener(nodeStoreListener);
Hyunsun Moonff55e812016-03-10 12:40:16 -0800255
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800256 leadershipService.withdraw(appId.name());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800257 eventExecutor.shutdown();
Hyunsun Moon479b7752016-05-06 20:13:28 -0700258
259 log.info("Stopped");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800260 }
261
262 /**
Hyunsun Moonff55e812016-03-10 12:40:16 -0800263 * Adds or updates a new node to the service.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800264 *
265 * @param node cordvtn node
266 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800267 public void addOrUpdateNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800268 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800269 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800270 }
271
272 /**
273 * Deletes a node from the service.
274 *
275 * @param node cordvtn node
276 */
277 public void deleteNode(CordVtnNode node) {
278 checkNotNull(node);
279
Hyunsun Moon126171d2016-02-09 01:55:48 -0800280 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800281 disconnectOvsdb(node);
282 }
283
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800284 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800285 }
286
287 /**
288 * Initiates node to serve virtual tenant network.
289 *
290 * @param node cordvtn node
291 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800292 private void initNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800293 checkNotNull(node);
294
Hyunsun Moonff55e812016-03-10 12:40:16 -0800295 NodeState state = (NodeState) node.state();
296 log.debug("Processing node: {} state: {}", node.hostname(), state);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800297
Hyunsun Mooncb799442016-01-15 20:03:18 -0800298 state.process(this, node);
299 }
300
301 /**
302 * Returns node initialization state.
303 *
304 * @param node cordvtn node
305 * @return true if initial node setup is completed, otherwise false
306 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800307 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800308 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800309 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800310 }
311
312 /**
Hyunsun Mooncbc885f2016-03-08 15:37:57 -0800313 * Flush flows installed by cordvtn.
314 */
315 public void flushRules() {
316 ruleInstaller.flushRules();
317 }
318
319 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800320 * Returns if current node state saved in nodeStore is COMPLETE or not.
321 *
322 * @param node cordvtn node
323 * @return true if it's complete state, otherwise false
324 */
325 private boolean isNodeStateComplete(CordVtnNode node) {
326 checkNotNull(node);
327
328 // the state saved in nodeStore can be wrong if IP address settings are changed
329 // after the node init has been completed since there's no way to detect it
330 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800331 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
332 CordVtnNodeState state = versionedNode.value().state();
333 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800334 }
335
336 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800337 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800338 *
339 * @param node cordvtn node
340 * @return string including detailed node init state
341 */
342 public String checkNodeInitState(CordVtnNode node) {
343 checkNotNull(node);
344
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800345 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800346 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800347 return null;
348 }
349
Hyunsun Moon479b7752016-05-06 20:13:28 -0700350 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800351 if (session == null) {
352 log.debug("Failed to SSH to {}", node.hostname());
353 return null;
354 }
355
Hyunsun Moon479b7752016-05-06 20:13:28 -0700356 Set<IpAddress> intBrIps = getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800357 String result = String.format(
Hyunsun Moon479b7752016-05-06 20:13:28 -0700358 "Current state : %s%n" +
359 "br-int created and connected to ONOS : %s (%s)%n" +
360 "VXLAN interface added to br-int : %s%n" +
361 "Data plane interface is added to br-int and enabled : %s (%s)%n" +
362 "IP flushed from data plane interface : %s (%s)%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800363 "Data plane IP added to br-int : %s (%s)%n" +
364 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon479b7752016-05-06 20:13:28 -0700365 node.state(),
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800366 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800367 isTunnelIntfCreated(node) ? OK : NO,
368 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
Hyunsun Moon479b7752016-05-06 20:13:28 -0700369 isInterfaceUp(session, node.dpIntf()) &&
370 getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO, node.dpIntf(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800371 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
372 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
373
Hyunsun Moon479b7752016-05-06 20:13:28 -0700374 disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800375
376 return result;
377 }
378
379 /**
380 * Returns the number of the nodes known to the service.
381 *
382 * @return number of nodes
383 */
384 public int getNodeCount() {
385 return nodeStore.size();
386 }
387
388 /**
389 * Returns all nodes known to the service.
390 *
391 * @return list of nodes
392 */
393 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800394 return nodeStore.values().stream()
395 .map(Versioned::value)
396 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800397 }
398
399 /**
400 * Returns cordvtn node associated with a given OVSDB device.
401 *
402 * @param ovsdbId OVSDB device id
403 * @return cordvtn node, null if it fails to find the node
404 */
405 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
406 return getNodes().stream()
407 .filter(node -> node.ovsdbId().equals(ovsdbId))
408 .findFirst().orElse(null);
409 }
410
411 /**
412 * Returns cordvtn node associated with a given integration bridge.
413 *
414 * @param bridgeId device id of integration bridge
415 * @return cordvtn node, null if it fails to find the node
416 */
417 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
418 return getNodes().stream()
419 .filter(node -> node.intBrId().equals(bridgeId))
420 .findFirst().orElse(null);
421 }
422
423 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800424 * Sets a new state for a given cordvtn node.
425 *
426 * @param node cordvtn node
427 * @param newState new node state
428 */
429 private void setNodeState(CordVtnNode node, NodeState newState) {
430 checkNotNull(node);
431
Hyunsun Moonff55e812016-03-10 12:40:16 -0800432 log.debug("Changed {} state: {}", node.hostname(), newState);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800433 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800434 }
435
436 /**
437 * Checks current state of a given cordvtn node and returns it.
438 *
439 * @param node cordvtn node
440 * @return node state
441 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800442 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800443 checkNotNull(node);
444
Hyunsun Moon126171d2016-02-09 01:55:48 -0800445 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
446 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800447 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800448 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
449 return NodeState.PORTS_ADDED;
450 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800451 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800452 } else {
453 return NodeState.INIT;
454 }
455 }
456
457 /**
458 * Performs tasks after node initialization.
459 * It disconnects unnecessary OVSDB connection and installs initial flow
460 * rules on the device.
461 *
462 * @param node cordvtn node
463 */
464 private void postInit(CordVtnNode node) {
465 disconnectOvsdb(node);
466
Hyunsun Moon126171d2016-02-09 01:55:48 -0800467 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800468
469 // add existing hosts to the service
470 deviceService.getPorts(node.intBrId()).stream()
471 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
472 port.isEnabled())
473 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
474
475 // remove stale hosts from the service
476 hostService.getHosts().forEach(host -> {
477 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
478 if (port == null) {
479 cordVtnService.removeServiceVm(getConnectPoint(host));
480 }
481 });
482
483 log.info("Finished init {}", node.hostname());
484 }
485
486 /**
487 * Returns port name.
488 *
489 * @param port port
490 * @return port name
491 */
492 private String getPortName(Port port) {
493 return port.annotations().value("portName");
494 }
495
496 /**
497 * Returns connection state of OVSDB server for a given node.
498 *
499 * @param node cordvtn node
500 * @return true if it is connected, false otherwise
501 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800502 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800503 checkNotNull(node);
504
505 OvsdbClientService ovsdbClient = getOvsdbClient(node);
506 return deviceService.isAvailable(node.ovsdbId()) &&
507 ovsdbClient != null && ovsdbClient.isConnected();
508 }
509
510 /**
511 * Connects to OVSDB server for a given node.
512 *
513 * @param node cordvtn node
514 */
515 private void connectOvsdb(CordVtnNode node) {
516 checkNotNull(node);
517
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800518 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800519 log.warn("Node {} does not exist", node.hostname());
520 return;
521 }
522
Hyunsun Moon126171d2016-02-09 01:55:48 -0800523 if (!isOvsdbConnected(node)) {
524 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800525 }
526 }
527
528 /**
529 * Disconnects OVSDB server for a given node.
530 *
531 * @param node cordvtn node
532 */
533 private void disconnectOvsdb(CordVtnNode node) {
534 checkNotNull(node);
535
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800536 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800537 log.warn("Node {} does not exist", node.hostname());
538 return;
539 }
540
Hyunsun Moon126171d2016-02-09 01:55:48 -0800541 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800542 OvsdbClientService ovsdbClient = getOvsdbClient(node);
543 ovsdbClient.disconnect();
544 }
545 }
546
547 /**
548 * Returns OVSDB client for a given node.
549 *
550 * @param node cordvtn node
551 * @return OVSDB client, or null if it fails to get OVSDB client
552 */
553 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
554 checkNotNull(node);
555
556 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800557 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800558 if (ovsdbClient == null) {
559 log.trace("Couldn't find OVSDB client for {}", node.hostname());
560 }
561 return ovsdbClient;
562 }
563
564 /**
565 * Creates an integration bridge for a given node.
566 *
567 * @param node cordvtn node
568 */
569 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800570 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800571 return;
572 }
573
574 List<ControllerInfo> controllers = new ArrayList<>();
575 Sets.newHashSet(clusterService.getNodes()).stream()
576 .forEach(controller -> {
577 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
578 controllers.add(ctrlInfo);
579 });
580
581 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
582
583 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700584 Device device = deviceService.getDevice(node.ovsdbId());
585 if (device.is(BridgeConfig.class)) {
586 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
587 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
588 } else {
589 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
590 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800591 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800592 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800593 }
594 }
595
596 /**
597 * Creates tunnel interface to the integration bridge for a given node.
598 *
599 * @param node cordvtn node
600 */
601 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800602 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800603 return;
604 }
605
606 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
607 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
608 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
609 }
610
611 TunnelDescription description = new DefaultTunnelDescription(
612 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
613 optionBuilder.build());
614
615 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700616 Device device = deviceService.getDevice(node.ovsdbId());
617 if (device.is(TunnelConfig.class)) {
618 TunnelConfig tunnelConfig = device.as(TunnelConfig.class);
619 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
620 } else {
621 log.warn("The tunneling behaviour is not supported in device {}", device.id().toString());
622 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800623 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800624 log.warn("Failed to create tunnel interface on {}", node.hostname());
625 }
626 }
627
628 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800629 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800630 *
631 * @param node cordvtn node
632 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800633 private void addDataPlaneInterface(CordVtnNode node) {
634 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800635 return;
636 }
637
Hyunsun Moon479b7752016-05-06 20:13:28 -0700638 Session session = connect(node.sshInfo());
639 if (session == null) {
640 log.debug("Failed to SSH to {}", node.hostname());
641 return;
642 }
643
644 if (!isInterfaceUp(session, node.dpIntf())) {
645 log.warn("Interface {} is not available", node.dpIntf());
646 return;
647 }
648 disconnect(session);
649
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800650 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700651 Device device = deviceService.getDevice(node.ovsdbId());
652 if (device.is(BridgeConfig.class)) {
653 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
654 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
655 } else {
656 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
657 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800658 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800659 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
660 }
661 }
662
663 /**
664 * Flushes IP address from data plane interface and adds data plane IP address
665 * to integration bridge.
666 *
667 * @param node cordvtn node
668 */
669 private void setIpAddress(CordVtnNode node) {
Hyunsun Moon479b7752016-05-06 20:13:28 -0700670 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800671 if (session == null) {
672 log.debug("Failed to SSH to {}", node.hostname());
673 return;
674 }
675
Hyunsun Moon479b7752016-05-06 20:13:28 -0700676 getCurrentIps(session, DEFAULT_BRIDGE).stream()
Hyunsun Moonf2760522016-03-04 19:24:08 -0800677 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
678 .filter(ip -> !ip.equals(node.dpIp().ip()))
Hyunsun Moon479b7752016-05-06 20:13:28 -0700679 .forEach(ip -> deleteIp(session, ip, DEFAULT_BRIDGE));
Hyunsun Moonf2760522016-03-04 19:24:08 -0800680
Hyunsun Moon479b7752016-05-06 20:13:28 -0700681 boolean result = flushIp(session, node.dpIntf()) &&
682 setInterfaceUp(session, node.dpIntf()) &&
683 addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
684 addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
685 setInterfaceUp(session, DEFAULT_BRIDGE);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800686
Hyunsun Moon479b7752016-05-06 20:13:28 -0700687 disconnect(session);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800688
689 if (result) {
690 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800691 }
692 }
693
694 /**
695 * Checks if integration bridge exists and available.
696 *
697 * @param node cordvtn node
698 * @return true if the bridge is available, false otherwise
699 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800700 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800701 return (deviceService.getDevice(node.intBrId()) != null
702 && deviceService.isAvailable(node.intBrId()));
703 }
704
705 /**
706 * Checks if tunnel interface exists.
707 *
708 * @param node cordvtn node
709 * @return true if the interface exists, false otherwise
710 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800711 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800712 return deviceService.getPorts(node.intBrId())
713 .stream()
714 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
715 p.isEnabled())
716 .findAny().isPresent();
717 }
718
719 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800720 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800721 *
722 * @param node cordvtn node
723 * @return true if the interface exists, false otherwise
724 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800725 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800726 return deviceService.getPorts(node.intBrId())
727 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800728 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800729 p.isEnabled())
730 .findAny().isPresent();
731 }
732
733 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800734 * Checks if the IP addresses are correctly set.
735 *
736 * @param node cordvtn node
737 * @return true if the IP is set, false otherwise
738 */
739 private boolean isIpAddressSet(CordVtnNode node) {
Hyunsun Moon479b7752016-05-06 20:13:28 -0700740 Session session = connect(node.sshInfo());
Hyunsun Moon126171d2016-02-09 01:55:48 -0800741 if (session == null) {
742 log.debug("Failed to SSH to {}", node.hostname());
743 return false;
744 }
745
Hyunsun Moon479b7752016-05-06 20:13:28 -0700746 Set<IpAddress> intBrIps = getCurrentIps(session, DEFAULT_BRIDGE);
747 boolean result = getCurrentIps(session, node.dpIntf()).isEmpty() &&
748 isInterfaceUp(session, node.dpIntf()) &&
Hyunsun Moon126171d2016-02-09 01:55:48 -0800749 intBrIps.contains(node.dpIp().ip()) &&
750 intBrIps.contains(node.localMgmtIp().ip()) &&
Hyunsun Moon479b7752016-05-06 20:13:28 -0700751 isInterfaceUp(session, DEFAULT_BRIDGE);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800752
Hyunsun Moon479b7752016-05-06 20:13:28 -0700753 disconnect(session);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800754 return result;
755 }
756
757 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800758 * Returns connect point of a given port.
759 *
760 * @param port port
761 * @return connect point
762 */
763 private ConnectPoint getConnectPoint(Port port) {
764 return new ConnectPoint(port.element().id(), port.number());
765 }
766
767 /**
768 * Returns connect point of a given host.
769 *
770 * @param host host
771 * @return connect point
772 */
773 private ConnectPoint getConnectPoint(Host host) {
774 return new ConnectPoint(host.location().deviceId(), host.location().port());
775 }
776
777 private class OvsdbHandler implements ConnectionHandler<Device> {
778
779 @Override
780 public void connected(Device device) {
781 CordVtnNode node = getNodeByOvsdbId(device.id());
782 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800783 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800784 } else {
785 log.debug("{} is detected on unregistered node, ignore it.", device.id());
786 }
787 }
788
789 @Override
790 public void disconnected(Device device) {
791 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800792 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800793 adminService.removeDevice(device.id());
794 }
795 }
796 }
797
798 private class BridgeHandler implements ConnectionHandler<Device> {
799
800 @Override
801 public void connected(Device device) {
802 CordVtnNode node = getNodeByBridgeId(device.id());
803 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800804 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800805 } else {
806 log.debug("{} is detected on unregistered node, ignore it.", device.id());
807 }
808 }
809
810 @Override
811 public void disconnected(Device device) {
812 CordVtnNode node = getNodeByBridgeId(device.id());
813 if (node != null) {
814 log.debug("Integration Bridge is disconnected from {}", node.hostname());
815 setNodeState(node, NodeState.INCOMPLETE);
816 }
817 }
818
819 /**
820 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800821 * If the added port is tunnel or data plane interface, proceed to the remaining
822 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800823 *
824 * @param port port
825 */
826 public void portAdded(Port port) {
827 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
828 String portName = getPortName(port);
829
830 if (node == null) {
831 log.debug("{} is added to unregistered node, ignore it.", portName);
832 return;
833 }
834
Hyunsun Moonff55e812016-03-10 12:40:16 -0800835 log.info("Port {} is added to {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800836
837 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800838 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800839 cordVtnService.addServiceVm(node, getConnectPoint(port));
840 } else {
841 log.debug("VM is detected on incomplete node, ignore it.", portName);
842 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800843 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
844 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800845 }
846 }
847
848 /**
849 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800850 * If the removed port is tunnel or data plane interface, proceed to the remaining
851 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800852 *
853 * @param port port
854 */
855 public void portRemoved(Port port) {
856 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
857 String portName = getPortName(port);
858
859 if (node == null) {
860 return;
861 }
862
Hyunsun Moonff55e812016-03-10 12:40:16 -0800863 log.info("Port {} is removed from {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800864
865 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800866 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800867 cordVtnService.removeServiceVm(getConnectPoint(port));
868 } else {
869 log.debug("VM is vanished from incomplete node, ignore it.", portName);
870 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800871 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800872 setNodeState(node, NodeState.INCOMPLETE);
873 }
874 }
875 }
876
877 private class InternalDeviceListener implements DeviceListener {
878
879 @Override
880 public void event(DeviceEvent event) {
881
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800882 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
883 if (!Objects.equals(localNodeId, leaderNodeId)) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800884 // do not allow to proceed without leadership
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800885 return;
886 }
887
Hyunsun Mooncb799442016-01-15 20:03:18 -0800888 Device device = event.subject();
889 ConnectionHandler<Device> handler =
890 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
891
892 switch (event.type()) {
893 case PORT_ADDED:
Hyunsun Moonff55e812016-03-10 12:40:16 -0800894 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800895 break;
896 case PORT_UPDATED:
897 if (!event.port().isEnabled()) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800898 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800899 }
900 break;
901 case DEVICE_ADDED:
902 case DEVICE_AVAILABILITY_CHANGED:
903 if (deviceService.isAvailable(device.id())) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800904 eventExecutor.execute(() -> handler.connected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800905 } else {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800906 eventExecutor.execute(() -> handler.disconnected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800907 }
908 break;
909 default:
910 break;
911 }
912 }
913 }
914
915 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800916 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800917 */
918 private void readConfiguration() {
919 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800920 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800921 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800922 return;
923 }
924
Hyunsun Moonff55e812016-03-10 12:40:16 -0800925 config.cordVtnNodes().forEach(this::addOrUpdateNode);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800926 }
927
928 private class InternalConfigListener implements NetworkConfigListener {
929
930 @Override
931 public void event(NetworkConfigEvent event) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800932 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
933 if (!Objects.equals(localNodeId, leaderNodeId)) {
934 // do not allow to proceed without leadership
935 return;
936 }
937
Hyunsun Mooncb799442016-01-15 20:03:18 -0800938 if (!event.configClass().equals(CordVtnConfig.class)) {
939 return;
940 }
941
942 switch (event.type()) {
943 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800944 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800945 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
946 break;
947 default:
948 break;
949 }
950 }
951 }
Hyunsun Moonff55e812016-03-10 12:40:16 -0800952
953 private class InternalMapListener implements MapEventListener<String, CordVtnNode> {
954
955 @Override
956 public void event(MapEvent<String, CordVtnNode> event) {
957 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
958 if (!Objects.equals(localNodeId, leaderNodeId)) {
959 // do not allow to proceed without leadership
960 return;
961 }
962
963 CordVtnNode oldNode;
964 CordVtnNode newNode;
965
966 switch (event.type()) {
967 case UPDATE:
968 oldNode = event.oldValue().value();
969 newNode = event.newValue().value();
970
Hyunsun Moon479b7752016-05-06 20:13:28 -0700971 log.info("Reloaded {}", newNode.hostname());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800972 if (!newNode.equals(oldNode)) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800973 log.debug("New node: {}", newNode);
974 }
Hyunsun Moon479b7752016-05-06 20:13:28 -0700975 // performs init procedure even if the node is not changed
976 // for robustness since it's no harm to run init procedure
977 // multiple times
Hyunsun Moonff55e812016-03-10 12:40:16 -0800978 eventExecutor.execute(() -> initNode(newNode));
979 break;
980 case INSERT:
981 newNode = event.newValue().value();
982 log.info("Added {}", newNode.hostname());
983 eventExecutor.execute(() -> initNode(newNode));
984 break;
985 case REMOVE:
986 oldNode = event.oldValue().value();
Hyunsun Moon479b7752016-05-06 20:13:28 -0700987 log.info("Removed {}", oldNode.hostname());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800988 break;
989 default:
990 break;
991 }
992 }
993 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800994}