blob: 8314f9650266887bceab53dd57a7276871154c2d [file] [log] [blame]
Hyunsun Mooncb799442016-01-15 20:03:18 -08001/*
2 * Copyright 2014-2015 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 */
16package org.onosproject.cordvtn;
17
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 Mooncb799442016-01-15 20:03:18 -080032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080034import org.onosproject.net.ConnectPoint;
35import org.onosproject.net.DefaultAnnotations;
36import org.onosproject.net.Device;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.Host;
39import org.onosproject.net.Port;
40import org.onosproject.net.behaviour.BridgeConfig;
41import org.onosproject.net.behaviour.BridgeName;
42import org.onosproject.net.behaviour.ControllerInfo;
43import org.onosproject.net.behaviour.DefaultTunnelDescription;
44import org.onosproject.net.behaviour.TunnelConfig;
45import org.onosproject.net.behaviour.TunnelDescription;
46import org.onosproject.net.behaviour.TunnelName;
Hyunsun Mooncb799442016-01-15 20:03:18 -080047import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
50import org.onosproject.net.config.NetworkConfigService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080051import org.onosproject.net.device.DeviceAdminService;
52import org.onosproject.net.device.DeviceEvent;
53import org.onosproject.net.device.DeviceListener;
54import org.onosproject.net.device.DeviceService;
55import org.onosproject.net.driver.DriverHandler;
56import org.onosproject.net.driver.DriverService;
57import org.onosproject.net.flow.FlowRuleService;
58import org.onosproject.net.group.GroupService;
59import org.onosproject.net.host.HostService;
60import org.onosproject.ovsdb.controller.OvsdbClientService;
61import org.onosproject.ovsdb.controller.OvsdbController;
62import org.onosproject.ovsdb.controller.OvsdbNodeId;
63import org.onosproject.store.serializers.KryoNamespaces;
64import org.onosproject.store.service.ConsistentMap;
65import org.onosproject.store.service.Serializer;
66import org.onosproject.store.service.StorageService;
Hyunsun Moond05b32e2016-03-02 19:27:26 -080067import org.onosproject.store.service.Versioned;
Hyunsun Mooncb799442016-01-15 20:03:18 -080068import org.slf4j.Logger;
69
70import java.util.ArrayList;
71import java.util.HashMap;
72import java.util.List;
73import java.util.Map;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -080074import java.util.Objects;
Hyunsun Moon126171d2016-02-09 01:55:48 -080075import java.util.Set;
Hyunsun Mooncb799442016-01-15 20:03:18 -080076import java.util.concurrent.ExecutorService;
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -080077import java.util.stream.Collectors;
Hyunsun Mooncb799442016-01-15 20:03:18 -080078
79import static com.google.common.base.Preconditions.checkNotNull;
80import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
81import static org.onlab.util.Tools.groupedThreads;
82import static org.onosproject.net.Device.Type.SWITCH;
83import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
84import static org.slf4j.LoggerFactory.getLogger;
85
86/**
87 * Reads node information from the network config file and handles the config
88 * update events.
89 * Only a leader controller performs the node addition or deletion.
90 */
91@Component(immediate = true)
92@Service(value = CordVtnNodeManager.class)
93public class CordVtnNodeManager {
94
95 protected final Logger log = getLogger(getClass());
96
97 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
98 .register(KryoNamespaces.API)
Hyunsun Moon126171d2016-02-09 01:55:48 -080099 .register(KryoNamespaces.MISC)
Hyunsun Mooncb799442016-01-15 20:03:18 -0800100 .register(CordVtnNode.class)
Hyunsun Moon126171d2016-02-09 01:55:48 -0800101 .register(NodeState.class)
102 .register(SshAccessInfo.class)
103 .register(NetworkAddress.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800104
105 private static final String DEFAULT_BRIDGE = "br-int";
106 private static final String DEFAULT_TUNNEL = "vxlan";
107 private static final String VPORT_PREFIX = "tap";
108 private static final String OK = "OK";
109 private static final String NO = "NO";
110
111 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
112 {
113 put("key", "flow");
114 put("remote_ip", "flow");
115 }
116 };
117 private static final int DPID_BEGIN = 3;
118 private static final int OFPORT = 6653;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected CoreService coreService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected NetworkConfigRegistry configRegistry;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected NetworkConfigService configService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected StorageService storageService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected DeviceAdminService adminService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected OvsdbController controller;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected ClusterService clusterService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected DriverService driverService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected DeviceService deviceService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected HostService hostService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected FlowRuleService flowRuleService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800154 protected LeadershipService leadershipService;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected GroupService groupService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected CordVtnService cordVtnService;
161
Hyunsun Mooncb799442016-01-15 20:03:18 -0800162 private final ExecutorService eventExecutor =
163 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler"));
164
165 private final NetworkConfigListener configListener = new InternalConfigListener();
166 private final DeviceListener deviceListener = new InternalDeviceListener();
167
168 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
169 private final BridgeHandler bridgeHandler = new BridgeHandler();
170
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800171 private ConsistentMap<String, CordVtnNode> nodeStore;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800172 private CordVtnRuleInstaller ruleInstaller;
173 private ApplicationId appId;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800174 private NodeId localNodeId;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800175
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800176 private enum NodeState implements CordVtnNodeState {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800177
178 INIT {
179 @Override
180 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800181 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800182 nodeManager.connectOvsdb(node);
183 } else {
184 nodeManager.createIntegrationBridge(node);
185 }
186 }
187 },
188 BRIDGE_CREATED {
189 @Override
190 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800191 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800192 nodeManager.connectOvsdb(node);
193 } else {
194 nodeManager.createTunnelInterface(node);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800195 nodeManager.addDataPlaneInterface(node);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800196 }
197 }
198 },
Hyunsun Moon126171d2016-02-09 01:55:48 -0800199 PORTS_ADDED {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800200 @Override
201 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800202 nodeManager.setIpAddress(node);
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800203 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800204 },
Hyunsun Mooncb799442016-01-15 20:03:18 -0800205 COMPLETE {
206 @Override
207 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
208 nodeManager.postInit(node);
209 }
210 },
211 INCOMPLETE {
212 @Override
213 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
214 }
215 };
216
Hyunsun Mooncb799442016-01-15 20:03:18 -0800217 public abstract void process(CordVtnNodeManager nodeManager, CordVtnNode node);
218 }
219
220 @Activate
221 protected void active() {
222 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800223 localNodeId = clusterService.getLocalNode().id();
224 leadershipService.runForLeadership(appId.name());
225
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800226 nodeStore = storageService.<String, CordVtnNode>consistentMapBuilder()
Hyunsun Mooncb799442016-01-15 20:03:18 -0800227 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
228 .withName("cordvtn-nodestore")
229 .withApplicationId(appId)
230 .build();
231
232 ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
233 deviceService,
234 driverService,
235 groupService,
Hyunsun Moon7a9bb122016-03-08 18:07:52 -0800236 configRegistry,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800237 DEFAULT_TUNNEL);
238
239 deviceService.addListener(deviceListener);
240 configService.addListener(configListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800241 }
242
243 @Deactivate
244 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800245 configService.removeListener(configListener);
246 deviceService.removeListener(deviceListener);
247
248 eventExecutor.shutdown();
249 nodeStore.clear();
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800250 leadershipService.withdraw(appId.name());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800251 }
252
253 /**
254 * Adds a new node to the service.
255 *
256 * @param node cordvtn node
257 */
258 public void addNode(CordVtnNode node) {
259 checkNotNull(node);
260
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800261 // allow update node attributes
262 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800263 initNode(node);
264 }
265
266 /**
267 * Deletes a node from the service.
268 *
269 * @param node cordvtn node
270 */
271 public void deleteNode(CordVtnNode node) {
272 checkNotNull(node);
273
Hyunsun Moon126171d2016-02-09 01:55:48 -0800274 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800275 disconnectOvsdb(node);
276 }
277
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800278 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800279 }
280
281 /**
282 * Initiates node to serve virtual tenant network.
283 *
284 * @param node cordvtn node
285 */
286 public void initNode(CordVtnNode node) {
287 checkNotNull(node);
288
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800289 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800290 log.warn("Node {} does not exist, add node first", node.hostname());
291 return;
292 }
293
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800294 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
295 log.debug("Node init requested, local: {} leader: {}", localNodeId, leaderNodeId);
296 if (!Objects.equals(localNodeId, leaderNodeId)) {
297 // only the leader performs node init
298 return;
299 }
300
Hyunsun Moon126171d2016-02-09 01:55:48 -0800301 NodeState state = getNodeState(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800302 log.debug("Init node: {} state: {}", node.hostname(), state.toString());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800303 state.process(this, node);
304 }
305
306 /**
307 * Returns node initialization state.
308 *
309 * @param node cordvtn node
310 * @return true if initial node setup is completed, otherwise false
311 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800312 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800313 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800314 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800315 }
316
317 /**
Hyunsun Mooncbc885f2016-03-08 15:37:57 -0800318 * Flush flows installed by cordvtn.
319 */
320 public void flushRules() {
321 ruleInstaller.flushRules();
322 }
323
324 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800325 * Returns if current node state saved in nodeStore is COMPLETE or not.
326 *
327 * @param node cordvtn node
328 * @return true if it's complete state, otherwise false
329 */
330 private boolean isNodeStateComplete(CordVtnNode node) {
331 checkNotNull(node);
332
333 // the state saved in nodeStore can be wrong if IP address settings are changed
334 // after the node init has been completed since there's no way to detect it
335 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800336 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
337 CordVtnNodeState state = versionedNode.value().state();
338 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800339 }
340
341 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800342 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800343 *
344 * @param node cordvtn node
345 * @return string including detailed node init state
346 */
347 public String checkNodeInitState(CordVtnNode node) {
348 checkNotNull(node);
349
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800350 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800351 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800352 return null;
353 }
354
Hyunsun Moon126171d2016-02-09 01:55:48 -0800355 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
356 if (session == null) {
357 log.debug("Failed to SSH to {}", node.hostname());
358 return null;
359 }
360
361 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800362 String result = String.format(
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800363 "br-int created and connected : %s (%s)%n" +
Hyunsun Mooncb799442016-01-15 20:03:18 -0800364 "VXLAN interface created : %s%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800365 "Data plane interface added : %s (%s)%n" +
366 "IP flushed from %s : %s%n" +
367 "Data plane IP added to br-int : %s (%s)%n" +
368 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800369 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800370 isTunnelIntfCreated(node) ? OK : NO,
371 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
372 node.dpIntf(),
373 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
374 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
375 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
376
377 RemoteIpCommandUtil.disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800378
379 return result;
380 }
381
382 /**
383 * Returns the number of the nodes known to the service.
384 *
385 * @return number of nodes
386 */
387 public int getNodeCount() {
388 return nodeStore.size();
389 }
390
391 /**
392 * Returns all nodes known to the service.
393 *
394 * @return list of nodes
395 */
396 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800397 return nodeStore.values().stream()
398 .map(Versioned::value)
399 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800400 }
401
402 /**
403 * Returns cordvtn node associated with a given OVSDB device.
404 *
405 * @param ovsdbId OVSDB device id
406 * @return cordvtn node, null if it fails to find the node
407 */
408 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
409 return getNodes().stream()
410 .filter(node -> node.ovsdbId().equals(ovsdbId))
411 .findFirst().orElse(null);
412 }
413
414 /**
415 * Returns cordvtn node associated with a given integration bridge.
416 *
417 * @param bridgeId device id of integration bridge
418 * @return cordvtn node, null if it fails to find the node
419 */
420 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
421 return getNodes().stream()
422 .filter(node -> node.intBrId().equals(bridgeId))
423 .findFirst().orElse(null);
424 }
425
426 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800427 * Sets a new state for a given cordvtn node.
428 *
429 * @param node cordvtn node
430 * @param newState new node state
431 */
432 private void setNodeState(CordVtnNode node, NodeState newState) {
433 checkNotNull(node);
434
435 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800436 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800437 newState.process(this, node);
438 }
439
440 /**
441 * Checks current state of a given cordvtn node and returns it.
442 *
443 * @param node cordvtn node
444 * @return node state
445 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800446 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800447 checkNotNull(node);
448
Hyunsun Moon126171d2016-02-09 01:55:48 -0800449 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
450 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800451 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800452 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
453 return NodeState.PORTS_ADDED;
454 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800455 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800456 } else {
457 return NodeState.INIT;
458 }
459 }
460
461 /**
462 * Performs tasks after node initialization.
463 * It disconnects unnecessary OVSDB connection and installs initial flow
464 * rules on the device.
465 *
466 * @param node cordvtn node
467 */
468 private void postInit(CordVtnNode node) {
469 disconnectOvsdb(node);
470
Hyunsun Moon126171d2016-02-09 01:55:48 -0800471 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800472
473 // add existing hosts to the service
474 deviceService.getPorts(node.intBrId()).stream()
475 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
476 port.isEnabled())
477 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
478
479 // remove stale hosts from the service
480 hostService.getHosts().forEach(host -> {
481 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
482 if (port == null) {
483 cordVtnService.removeServiceVm(getConnectPoint(host));
484 }
485 });
486
487 log.info("Finished init {}", node.hostname());
488 }
489
490 /**
491 * Returns port name.
492 *
493 * @param port port
494 * @return port name
495 */
496 private String getPortName(Port port) {
497 return port.annotations().value("portName");
498 }
499
500 /**
501 * Returns connection state of OVSDB server for a given node.
502 *
503 * @param node cordvtn node
504 * @return true if it is connected, false otherwise
505 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800506 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800507 checkNotNull(node);
508
509 OvsdbClientService ovsdbClient = getOvsdbClient(node);
510 return deviceService.isAvailable(node.ovsdbId()) &&
511 ovsdbClient != null && ovsdbClient.isConnected();
512 }
513
514 /**
515 * Connects to OVSDB server for a given node.
516 *
517 * @param node cordvtn node
518 */
519 private void connectOvsdb(CordVtnNode node) {
520 checkNotNull(node);
521
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800522 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800523 log.warn("Node {} does not exist", node.hostname());
524 return;
525 }
526
Hyunsun Moon126171d2016-02-09 01:55:48 -0800527 if (!isOvsdbConnected(node)) {
528 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800529 }
530 }
531
532 /**
533 * Disconnects OVSDB server for a given node.
534 *
535 * @param node cordvtn node
536 */
537 private void disconnectOvsdb(CordVtnNode node) {
538 checkNotNull(node);
539
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800540 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800541 log.warn("Node {} does not exist", node.hostname());
542 return;
543 }
544
Hyunsun Moon126171d2016-02-09 01:55:48 -0800545 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800546 OvsdbClientService ovsdbClient = getOvsdbClient(node);
547 ovsdbClient.disconnect();
548 }
549 }
550
551 /**
552 * Returns OVSDB client for a given node.
553 *
554 * @param node cordvtn node
555 * @return OVSDB client, or null if it fails to get OVSDB client
556 */
557 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
558 checkNotNull(node);
559
560 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800561 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800562 if (ovsdbClient == null) {
563 log.trace("Couldn't find OVSDB client for {}", node.hostname());
564 }
565 return ovsdbClient;
566 }
567
568 /**
569 * Creates an integration bridge for a given node.
570 *
571 * @param node cordvtn node
572 */
573 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800574 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800575 return;
576 }
577
578 List<ControllerInfo> controllers = new ArrayList<>();
579 Sets.newHashSet(clusterService.getNodes()).stream()
580 .forEach(controller -> {
581 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
582 controllers.add(ctrlInfo);
583 });
584
585 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
586
587 try {
588 DriverHandler handler = driverService.createHandler(node.ovsdbId());
589 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
590 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
591 } 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 {
616 DriverHandler handler = driverService.createHandler(node.ovsdbId());
617 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
618 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
619 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800620 log.warn("Failed to create tunnel interface on {}", node.hostname());
621 }
622 }
623
624 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800625 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800626 *
627 * @param node cordvtn node
628 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800629 private void addDataPlaneInterface(CordVtnNode node) {
630 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800631 return;
632 }
633
634 try {
635 DriverHandler handler = driverService.createHandler(node.ovsdbId());
636 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800637 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800638 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800639 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
640 }
641 }
642
643 /**
644 * Flushes IP address from data plane interface and adds data plane IP address
645 * to integration bridge.
646 *
647 * @param node cordvtn node
648 */
649 private void setIpAddress(CordVtnNode node) {
650 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
651 if (session == null) {
652 log.debug("Failed to SSH to {}", node.hostname());
653 return;
654 }
655
Hyunsun Moonf2760522016-03-04 19:24:08 -0800656 RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE).stream()
657 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
658 .filter(ip -> !ip.equals(node.dpIp().ip()))
659 .forEach(ip -> RemoteIpCommandUtil.deleteIp(session, ip, DEFAULT_BRIDGE));
660
Hyunsun Moon126171d2016-02-09 01:55:48 -0800661 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
662 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
663 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
664 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
665 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
666
667 RemoteIpCommandUtil.disconnect(session);
668
669 if (result) {
670 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800671 }
672 }
673
674 /**
675 * Checks if integration bridge exists and available.
676 *
677 * @param node cordvtn node
678 * @return true if the bridge is available, false otherwise
679 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800680 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800681 return (deviceService.getDevice(node.intBrId()) != null
682 && deviceService.isAvailable(node.intBrId()));
683 }
684
685 /**
686 * Checks if tunnel interface exists.
687 *
688 * @param node cordvtn node
689 * @return true if the interface exists, false otherwise
690 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800691 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800692 return deviceService.getPorts(node.intBrId())
693 .stream()
694 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
695 p.isEnabled())
696 .findAny().isPresent();
697 }
698
699 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800700 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800701 *
702 * @param node cordvtn node
703 * @return true if the interface exists, false otherwise
704 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800705 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800706 return deviceService.getPorts(node.intBrId())
707 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800708 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800709 p.isEnabled())
710 .findAny().isPresent();
711 }
712
713 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800714 * Checks if the IP addresses are correctly set.
715 *
716 * @param node cordvtn node
717 * @return true if the IP is set, false otherwise
718 */
719 private boolean isIpAddressSet(CordVtnNode node) {
720 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
721 if (session == null) {
722 log.debug("Failed to SSH to {}", node.hostname());
723 return false;
724 }
725
726 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
727 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
728 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
729 intBrIps.contains(node.dpIp().ip()) &&
730 intBrIps.contains(node.localMgmtIp().ip()) &&
731 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
732
733 RemoteIpCommandUtil.disconnect(session);
734 return result;
735 }
736
737 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800738 * Returns connect point of a given port.
739 *
740 * @param port port
741 * @return connect point
742 */
743 private ConnectPoint getConnectPoint(Port port) {
744 return new ConnectPoint(port.element().id(), port.number());
745 }
746
747 /**
748 * Returns connect point of a given host.
749 *
750 * @param host host
751 * @return connect point
752 */
753 private ConnectPoint getConnectPoint(Host host) {
754 return new ConnectPoint(host.location().deviceId(), host.location().port());
755 }
756
757 private class OvsdbHandler implements ConnectionHandler<Device> {
758
759 @Override
760 public void connected(Device device) {
761 CordVtnNode node = getNodeByOvsdbId(device.id());
762 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800763 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800764 } else {
765 log.debug("{} is detected on unregistered node, ignore it.", device.id());
766 }
767 }
768
769 @Override
770 public void disconnected(Device device) {
771 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800772 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800773 adminService.removeDevice(device.id());
774 }
775 }
776 }
777
778 private class BridgeHandler implements ConnectionHandler<Device> {
779
780 @Override
781 public void connected(Device device) {
782 CordVtnNode node = getNodeByBridgeId(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 CordVtnNode node = getNodeByBridgeId(device.id());
793 if (node != null) {
794 log.debug("Integration Bridge is disconnected from {}", node.hostname());
795 setNodeState(node, NodeState.INCOMPLETE);
796 }
797 }
798
799 /**
800 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800801 * If the added port is tunnel or data plane interface, proceed to the remaining
802 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800803 *
804 * @param port port
805 */
806 public void portAdded(Port port) {
807 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
808 String portName = getPortName(port);
809
810 if (node == null) {
811 log.debug("{} is added to unregistered node, ignore it.", portName);
812 return;
813 }
814
815 log.debug("Port {} is added to {}", portName, node.hostname());
816
817 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800818 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800819 cordVtnService.addServiceVm(node, getConnectPoint(port));
820 } else {
821 log.debug("VM is detected on incomplete node, ignore it.", portName);
822 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800823 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
824 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800825 }
826 }
827
828 /**
829 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800830 * If the removed port is tunnel or data plane interface, proceed to the remaining
831 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800832 *
833 * @param port port
834 */
835 public void portRemoved(Port port) {
836 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
837 String portName = getPortName(port);
838
839 if (node == null) {
840 return;
841 }
842
843 log.debug("Port {} is removed from {}", portName, node.hostname());
844
845 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800846 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800847 cordVtnService.removeServiceVm(getConnectPoint(port));
848 } else {
849 log.debug("VM is vanished from incomplete node, ignore it.", portName);
850 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800851 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800852 setNodeState(node, NodeState.INCOMPLETE);
853 }
854 }
855 }
856
857 private class InternalDeviceListener implements DeviceListener {
858
859 @Override
860 public void event(DeviceEvent event) {
861
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800862 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
863 if (!Objects.equals(localNodeId, leaderNodeId)) {
864 // only the leader processes events
865 return;
866 }
867
Hyunsun Mooncb799442016-01-15 20:03:18 -0800868 Device device = event.subject();
869 ConnectionHandler<Device> handler =
870 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
871
872 switch (event.type()) {
873 case PORT_ADDED:
874 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
875 break;
876 case PORT_UPDATED:
877 if (!event.port().isEnabled()) {
878 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
879 }
880 break;
881 case DEVICE_ADDED:
882 case DEVICE_AVAILABILITY_CHANGED:
883 if (deviceService.isAvailable(device.id())) {
884 eventExecutor.submit(() -> handler.connected(device));
885 } else {
886 eventExecutor.submit(() -> handler.disconnected(device));
887 }
888 break;
889 default:
890 break;
891 }
892 }
893 }
894
895 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800896 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800897 */
898 private void readConfiguration() {
899 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800900 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800901 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800902 return;
903 }
904
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800905 config.cordVtnNodes().forEach(this::addNode);
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800906 // TODO remove nodes if needed
Hyunsun Mooncb799442016-01-15 20:03:18 -0800907 }
908
909 private class InternalConfigListener implements NetworkConfigListener {
910
911 @Override
912 public void event(NetworkConfigEvent event) {
913 if (!event.configClass().equals(CordVtnConfig.class)) {
914 return;
915 }
916
917 switch (event.type()) {
918 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800919 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800920 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
921 break;
922 default:
923 break;
924 }
925 }
926 }
927}