blob: 9dd8c24e7a6b39755c43f4b4297a2514a41cfd3f [file] [log] [blame]
Hyunsun Moonb77b60f2016-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 Moon133fd792016-02-09 01:55:48 -080019import com.jcraft.jsch.Session;
Hyunsun Moonb77b60f2016-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 Moon133fd792016-02-09 01:55:48 -080026import org.onlab.packet.IpAddress;
27import org.onlab.packet.TpPort;
Hyunsun Moonb77b60f2016-01-15 20:03:18 -080028import org.onlab.util.ItemNotFoundException;
29import org.onlab.util.KryoNamespace;
30import org.onosproject.cluster.ClusterService;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.mastership.MastershipService;
34import 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 Moonb77b60f2016-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 Moonb77b60f2016-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;
67import org.slf4j.Logger;
68
69import java.util.ArrayList;
70import java.util.HashMap;
71import java.util.List;
72import java.util.Map;
Hyunsun Moon133fd792016-02-09 01:55:48 -080073import java.util.Set;
Hyunsun Moonb77b60f2016-01-15 20:03:18 -080074import java.util.concurrent.ExecutorService;
75
76import static com.google.common.base.Preconditions.checkNotNull;
77import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
78import static org.onlab.util.Tools.groupedThreads;
79import static org.onosproject.net.Device.Type.SWITCH;
80import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
81import static org.slf4j.LoggerFactory.getLogger;
82
83/**
84 * Reads node information from the network config file and handles the config
85 * update events.
86 * Only a leader controller performs the node addition or deletion.
87 */
88@Component(immediate = true)
89@Service(value = CordVtnNodeManager.class)
90public class CordVtnNodeManager {
91
92 protected final Logger log = getLogger(getClass());
93
94 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
95 .register(KryoNamespaces.API)
Hyunsun Moon133fd792016-02-09 01:55:48 -080096 .register(KryoNamespaces.MISC)
Hyunsun Moonb77b60f2016-01-15 20:03:18 -080097 .register(CordVtnNode.class)
Hyunsun Moon133fd792016-02-09 01:55:48 -080098 .register(NodeState.class)
99 .register(SshAccessInfo.class)
100 .register(NetworkAddress.class);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800101
102 private static final String DEFAULT_BRIDGE = "br-int";
103 private static final String DEFAULT_TUNNEL = "vxlan";
104 private static final String VPORT_PREFIX = "tap";
105 private static final String OK = "OK";
106 private static final String NO = "NO";
107
108 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
109 {
110 put("key", "flow");
111 put("remote_ip", "flow");
112 }
113 };
114 private static final int DPID_BEGIN = 3;
115 private static final int OFPORT = 6653;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected CoreService coreService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected NetworkConfigRegistry configRegistry;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected NetworkConfigService configService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected StorageService storageService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected DeviceAdminService adminService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected OvsdbController controller;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected ClusterService clusterService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected DriverService driverService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected DeviceService deviceService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected HostService hostService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected FlowRuleService flowRuleService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected MastershipService mastershipService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected GroupService groupService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected CordVtnService cordVtnService;
158
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800159 private final ExecutorService eventExecutor =
160 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler"));
161
162 private final NetworkConfigListener configListener = new InternalConfigListener();
163 private final DeviceListener deviceListener = new InternalDeviceListener();
164
165 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
166 private final BridgeHandler bridgeHandler = new BridgeHandler();
167
168 private ConsistentMap<CordVtnNode, NodeState> nodeStore;
169 private CordVtnRuleInstaller ruleInstaller;
170 private ApplicationId appId;
171
172 private enum NodeState {
173
174 INIT {
175 @Override
176 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800177 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800178 nodeManager.connectOvsdb(node);
179 } else {
180 nodeManager.createIntegrationBridge(node);
181 }
182 }
183 },
184 BRIDGE_CREATED {
185 @Override
186 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800187 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800188 nodeManager.connectOvsdb(node);
189 } else {
190 nodeManager.createTunnelInterface(node);
Hyunsun Moon133fd792016-02-09 01:55:48 -0800191 nodeManager.addDataPlaneInterface(node);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800192 }
193 }
194 },
Hyunsun Moon133fd792016-02-09 01:55:48 -0800195 PORTS_ADDED {
Hyunsun Moon177506f2016-01-21 00:54:52 -0800196 @Override
197 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800198 nodeManager.setIpAddress(node);
Hyunsun Moon177506f2016-01-21 00:54:52 -0800199 }
200
201 },
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800202 COMPLETE {
203 @Override
204 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
205 nodeManager.postInit(node);
206 }
207 },
208 INCOMPLETE {
209 @Override
210 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
211 }
212 };
213
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800214 public abstract void process(CordVtnNodeManager nodeManager, CordVtnNode node);
215 }
216
217 @Activate
218 protected void active() {
219 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800220 nodeStore = storageService.<CordVtnNode, NodeState>consistentMapBuilder()
221 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
222 .withName("cordvtn-nodestore")
223 .withApplicationId(appId)
224 .build();
225
226 ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
227 deviceService,
228 driverService,
229 groupService,
230 mastershipService,
231 DEFAULT_TUNNEL);
232
233 deviceService.addListener(deviceListener);
234 configService.addListener(configListener);
Hyunsun Moon746956f2016-01-24 21:47:06 -0800235 readConfiguration();
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800236 }
237
238 @Deactivate
239 protected void deactivate() {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800240 configService.removeListener(configListener);
241 deviceService.removeListener(deviceListener);
242
243 eventExecutor.shutdown();
244 nodeStore.clear();
245 }
246
247 /**
248 * Adds a new node to the service.
249 *
250 * @param node cordvtn node
251 */
252 public void addNode(CordVtnNode node) {
253 checkNotNull(node);
254
Hyunsun Moon133fd792016-02-09 01:55:48 -0800255 nodeStore.putIfAbsent(node, getNodeState(node));
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800256 initNode(node);
257 }
258
259 /**
260 * Deletes a node from the service.
261 *
262 * @param node cordvtn node
263 */
264 public void deleteNode(CordVtnNode node) {
265 checkNotNull(node);
266
Hyunsun Moon133fd792016-02-09 01:55:48 -0800267 if (isOvsdbConnected(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800268 disconnectOvsdb(node);
269 }
270
271 nodeStore.remove(node);
272 }
273
274 /**
275 * Initiates node to serve virtual tenant network.
276 *
277 * @param node cordvtn node
278 */
279 public void initNode(CordVtnNode node) {
280 checkNotNull(node);
281
282 if (!nodeStore.containsKey(node)) {
283 log.warn("Node {} does not exist, add node first", node.hostname());
284 return;
285 }
286
Hyunsun Moon133fd792016-02-09 01:55:48 -0800287 NodeState state = getNodeState(node);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800288 state.process(this, node);
289 }
290
291 /**
292 * Returns node initialization state.
293 *
294 * @param node cordvtn node
295 * @return true if initial node setup is completed, otherwise false
296 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800297 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800298 checkNotNull(node);
Hyunsun Moon133fd792016-02-09 01:55:48 -0800299 return nodeStore.containsKey(node) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800300 }
301
302 /**
303 * Returns detailed node initialization state.
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800304 *
305 * @param node cordvtn node
306 * @return string including detailed node init state
307 */
308 public String checkNodeInitState(CordVtnNode node) {
309 checkNotNull(node);
310
Hyunsun Moon133fd792016-02-09 01:55:48 -0800311 if (!nodeStore.containsKey(node)) {
312 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800313 return null;
314 }
315
Hyunsun Moon133fd792016-02-09 01:55:48 -0800316 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
317 if (session == null) {
318 log.debug("Failed to SSH to {}", node.hostname());
319 return null;
320 }
321
322 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800323 String result = String.format(
324 "Integration bridge created/connected : %s (%s)%n" +
325 "VXLAN interface created : %s%n" +
Hyunsun Moon133fd792016-02-09 01:55:48 -0800326 "Data plane interface added : %s (%s)%n" +
327 "IP flushed from %s : %s%n" +
328 "Data plane IP added to br-int : %s (%s)%n" +
329 "Local management IP added to br-int : %s (%s)",
330 isBrIntCreated(node) ? OK : NO, DEFAULT_BRIDGE,
331 isTunnelIntfCreated(node) ? OK : NO,
332 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
333 node.dpIntf(),
334 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
335 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
336 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
337
338 RemoteIpCommandUtil.disconnect(session);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800339
340 return result;
341 }
342
343 /**
344 * Returns the number of the nodes known to the service.
345 *
346 * @return number of nodes
347 */
348 public int getNodeCount() {
349 return nodeStore.size();
350 }
351
352 /**
353 * Returns all nodes known to the service.
354 *
355 * @return list of nodes
356 */
357 public List<CordVtnNode> getNodes() {
358 List<CordVtnNode> nodes = new ArrayList<>();
359 nodes.addAll(nodeStore.keySet());
360 return nodes;
361 }
362
363 /**
364 * Returns cordvtn node associated with a given OVSDB device.
365 *
366 * @param ovsdbId OVSDB device id
367 * @return cordvtn node, null if it fails to find the node
368 */
369 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
370 return getNodes().stream()
371 .filter(node -> node.ovsdbId().equals(ovsdbId))
372 .findFirst().orElse(null);
373 }
374
375 /**
376 * Returns cordvtn node associated with a given integration bridge.
377 *
378 * @param bridgeId device id of integration bridge
379 * @return cordvtn node, null if it fails to find the node
380 */
381 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
382 return getNodes().stream()
383 .filter(node -> node.intBrId().equals(bridgeId))
384 .findFirst().orElse(null);
385 }
386
387 /**
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800388 * Sets a new state for a given cordvtn node.
389 *
390 * @param node cordvtn node
391 * @param newState new node state
392 */
393 private void setNodeState(CordVtnNode node, NodeState newState) {
394 checkNotNull(node);
395
396 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
397
398 nodeStore.put(node, newState);
399 newState.process(this, node);
400 }
401
402 /**
403 * Checks current state of a given cordvtn node and returns it.
404 *
405 * @param node cordvtn node
406 * @return node state
407 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800408 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800409 checkNotNull(node);
410
Hyunsun Moon133fd792016-02-09 01:55:48 -0800411 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
412 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moon177506f2016-01-21 00:54:52 -0800413 return NodeState.COMPLETE;
Hyunsun Moon133fd792016-02-09 01:55:48 -0800414 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
415 return NodeState.PORTS_ADDED;
416 } else if (isBrIntCreated(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800417 return NodeState.BRIDGE_CREATED;
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800418 } else {
419 return NodeState.INIT;
420 }
421 }
422
423 /**
424 * Performs tasks after node initialization.
425 * It disconnects unnecessary OVSDB connection and installs initial flow
426 * rules on the device.
427 *
428 * @param node cordvtn node
429 */
430 private void postInit(CordVtnNode node) {
431 disconnectOvsdb(node);
432
Hyunsun Moon133fd792016-02-09 01:55:48 -0800433 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800434
435 // add existing hosts to the service
436 deviceService.getPorts(node.intBrId()).stream()
437 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
438 port.isEnabled())
439 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
440
441 // remove stale hosts from the service
442 hostService.getHosts().forEach(host -> {
443 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
444 if (port == null) {
445 cordVtnService.removeServiceVm(getConnectPoint(host));
446 }
447 });
448
449 log.info("Finished init {}", node.hostname());
450 }
451
452 /**
453 * Returns port name.
454 *
455 * @param port port
456 * @return port name
457 */
458 private String getPortName(Port port) {
459 return port.annotations().value("portName");
460 }
461
462 /**
463 * Returns connection state of OVSDB server for a given node.
464 *
465 * @param node cordvtn node
466 * @return true if it is connected, false otherwise
467 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800468 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800469 checkNotNull(node);
470
471 OvsdbClientService ovsdbClient = getOvsdbClient(node);
472 return deviceService.isAvailable(node.ovsdbId()) &&
473 ovsdbClient != null && ovsdbClient.isConnected();
474 }
475
476 /**
477 * Connects to OVSDB server for a given node.
478 *
479 * @param node cordvtn node
480 */
481 private void connectOvsdb(CordVtnNode node) {
482 checkNotNull(node);
483
484 if (!nodeStore.containsKey(node)) {
485 log.warn("Node {} does not exist", node.hostname());
486 return;
487 }
488
Hyunsun Moon133fd792016-02-09 01:55:48 -0800489 if (!isOvsdbConnected(node)) {
490 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800491 }
492 }
493
494 /**
495 * Disconnects OVSDB server for a given node.
496 *
497 * @param node cordvtn node
498 */
499 private void disconnectOvsdb(CordVtnNode node) {
500 checkNotNull(node);
501
502 if (!nodeStore.containsKey(node)) {
503 log.warn("Node {} does not exist", node.hostname());
504 return;
505 }
506
Hyunsun Moon133fd792016-02-09 01:55:48 -0800507 if (isOvsdbConnected(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800508 OvsdbClientService ovsdbClient = getOvsdbClient(node);
509 ovsdbClient.disconnect();
510 }
511 }
512
513 /**
514 * Returns OVSDB client for a given node.
515 *
516 * @param node cordvtn node
517 * @return OVSDB client, or null if it fails to get OVSDB client
518 */
519 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
520 checkNotNull(node);
521
522 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon133fd792016-02-09 01:55:48 -0800523 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800524 if (ovsdbClient == null) {
525 log.trace("Couldn't find OVSDB client for {}", node.hostname());
526 }
527 return ovsdbClient;
528 }
529
530 /**
531 * Creates an integration bridge for a given node.
532 *
533 * @param node cordvtn node
534 */
535 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800536 if (isBrIntCreated(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800537 return;
538 }
539
540 List<ControllerInfo> controllers = new ArrayList<>();
541 Sets.newHashSet(clusterService.getNodes()).stream()
542 .forEach(controller -> {
543 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
544 controllers.add(ctrlInfo);
545 });
546
547 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
548
549 try {
550 DriverHandler handler = driverService.createHandler(node.ovsdbId());
551 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
552 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
553 } catch (ItemNotFoundException e) {
Hyunsun Moon177506f2016-01-21 00:54:52 -0800554 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800555 }
556 }
557
558 /**
559 * Creates tunnel interface to the integration bridge for a given node.
560 *
561 * @param node cordvtn node
562 */
563 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800564 if (isTunnelIntfCreated(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800565 return;
566 }
567
568 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
569 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
570 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
571 }
572
573 TunnelDescription description = new DefaultTunnelDescription(
574 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
575 optionBuilder.build());
576
577 try {
578 DriverHandler handler = driverService.createHandler(node.ovsdbId());
579 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
580 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
581 } catch (ItemNotFoundException e) {
Hyunsun Moon177506f2016-01-21 00:54:52 -0800582 log.warn("Failed to create tunnel interface on {}", node.hostname());
583 }
584 }
585
586 /**
Hyunsun Moon133fd792016-02-09 01:55:48 -0800587 * Adds data plane interface to a given node.
Hyunsun Moon177506f2016-01-21 00:54:52 -0800588 *
589 * @param node cordvtn node
590 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800591 private void addDataPlaneInterface(CordVtnNode node) {
592 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moon177506f2016-01-21 00:54:52 -0800593 return;
594 }
595
596 try {
597 DriverHandler handler = driverService.createHandler(node.ovsdbId());
598 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon133fd792016-02-09 01:55:48 -0800599 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
Hyunsun Moon177506f2016-01-21 00:54:52 -0800600 } catch (ItemNotFoundException e) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800601 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
602 }
603 }
604
605 /**
606 * Flushes IP address from data plane interface and adds data plane IP address
607 * to integration bridge.
608 *
609 * @param node cordvtn node
610 */
611 private void setIpAddress(CordVtnNode node) {
612 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
613 if (session == null) {
614 log.debug("Failed to SSH to {}", node.hostname());
615 return;
616 }
617
618 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
619 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
620 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
621 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
622 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
623
624 RemoteIpCommandUtil.disconnect(session);
625
626 if (result) {
627 setNodeState(node, NodeState.COMPLETE);
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800628 }
629 }
630
631 /**
632 * Checks if integration bridge exists and available.
633 *
634 * @param node cordvtn node
635 * @return true if the bridge is available, false otherwise
636 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800637 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800638 return (deviceService.getDevice(node.intBrId()) != null
639 && deviceService.isAvailable(node.intBrId()));
640 }
641
642 /**
643 * Checks if tunnel interface exists.
644 *
645 * @param node cordvtn node
646 * @return true if the interface exists, false otherwise
647 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800648 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800649 return deviceService.getPorts(node.intBrId())
650 .stream()
651 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
652 p.isEnabled())
653 .findAny().isPresent();
654 }
655
656 /**
Hyunsun Moon133fd792016-02-09 01:55:48 -0800657 * Checks if data plane interface exists.
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800658 *
659 * @param node cordvtn node
660 * @return true if the interface exists, false otherwise
661 */
Hyunsun Moon133fd792016-02-09 01:55:48 -0800662 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800663 return deviceService.getPorts(node.intBrId())
664 .stream()
Hyunsun Moon133fd792016-02-09 01:55:48 -0800665 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800666 p.isEnabled())
667 .findAny().isPresent();
668 }
669
670 /**
Hyunsun Moon133fd792016-02-09 01:55:48 -0800671 * Checks if the IP addresses are correctly set.
672 *
673 * @param node cordvtn node
674 * @return true if the IP is set, false otherwise
675 */
676 private boolean isIpAddressSet(CordVtnNode node) {
677 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
678 if (session == null) {
679 log.debug("Failed to SSH to {}", node.hostname());
680 return false;
681 }
682
683 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
684 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
685 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
686 intBrIps.contains(node.dpIp().ip()) &&
687 intBrIps.contains(node.localMgmtIp().ip()) &&
688 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
689
690 RemoteIpCommandUtil.disconnect(session);
691 return result;
692 }
693
694 /**
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800695 * Returns connect point of a given port.
696 *
697 * @param port port
698 * @return connect point
699 */
700 private ConnectPoint getConnectPoint(Port port) {
701 return new ConnectPoint(port.element().id(), port.number());
702 }
703
704 /**
705 * Returns connect point of a given host.
706 *
707 * @param host host
708 * @return connect point
709 */
710 private ConnectPoint getConnectPoint(Host host) {
711 return new ConnectPoint(host.location().deviceId(), host.location().port());
712 }
713
714 private class OvsdbHandler implements ConnectionHandler<Device> {
715
716 @Override
717 public void connected(Device device) {
718 CordVtnNode node = getNodeByOvsdbId(device.id());
719 if (node != null) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800720 setNodeState(node, getNodeState(node));
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800721 } else {
722 log.debug("{} is detected on unregistered node, ignore it.", device.id());
723 }
724 }
725
726 @Override
727 public void disconnected(Device device) {
728 if (!deviceService.isAvailable(device.id())) {
729 adminService.removeDevice(device.id());
730 }
731 }
732 }
733
734 private class BridgeHandler implements ConnectionHandler<Device> {
735
736 @Override
737 public void connected(Device device) {
738 CordVtnNode node = getNodeByBridgeId(device.id());
739 if (node != null) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800740 setNodeState(node, getNodeState(node));
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800741 } else {
742 log.debug("{} is detected on unregistered node, ignore it.", device.id());
743 }
744 }
745
746 @Override
747 public void disconnected(Device device) {
748 CordVtnNode node = getNodeByBridgeId(device.id());
749 if (node != null) {
750 log.debug("Integration Bridge is disconnected from {}", node.hostname());
751 setNodeState(node, NodeState.INCOMPLETE);
752 }
753 }
754
755 /**
756 * Handles port added situation.
Hyunsun Moon133fd792016-02-09 01:55:48 -0800757 * If the added port is tunnel or data plane interface, proceed to the remaining
758 * node initialization. Otherwise, do nothing.
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800759 *
760 * @param port port
761 */
762 public void portAdded(Port port) {
763 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
764 String portName = getPortName(port);
765
766 if (node == null) {
767 log.debug("{} is added to unregistered node, ignore it.", portName);
768 return;
769 }
770
771 log.debug("Port {} is added to {}", portName, node.hostname());
772
773 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800774 if (isNodeInitComplete(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800775 cordVtnService.addServiceVm(node, getConnectPoint(port));
776 } else {
777 log.debug("VM is detected on incomplete node, ignore it.", portName);
778 }
Hyunsun Moon133fd792016-02-09 01:55:48 -0800779 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
780 setNodeState(node, getNodeState(node));
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800781 }
782 }
783
784 /**
785 * Handles port removed situation.
Hyunsun Moon133fd792016-02-09 01:55:48 -0800786 * If the removed port is tunnel or data plane interface, proceed to the remaining
787 * node initialization.Others, do nothing.
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800788 *
789 * @param port port
790 */
791 public void portRemoved(Port port) {
792 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
793 String portName = getPortName(port);
794
795 if (node == null) {
796 return;
797 }
798
799 log.debug("Port {} is removed from {}", portName, node.hostname());
800
801 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800802 if (isNodeInitComplete(node)) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800803 cordVtnService.removeServiceVm(getConnectPoint(port));
804 } else {
805 log.debug("VM is vanished from incomplete node, ignore it.", portName);
806 }
Hyunsun Moon133fd792016-02-09 01:55:48 -0800807 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800808 setNodeState(node, NodeState.INCOMPLETE);
809 }
810 }
811 }
812
813 private class InternalDeviceListener implements DeviceListener {
814
815 @Override
816 public void event(DeviceEvent event) {
817
818 Device device = event.subject();
819 ConnectionHandler<Device> handler =
820 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
821
822 switch (event.type()) {
823 case PORT_ADDED:
824 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
825 break;
826 case PORT_UPDATED:
827 if (!event.port().isEnabled()) {
828 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
829 }
830 break;
831 case DEVICE_ADDED:
832 case DEVICE_AVAILABILITY_CHANGED:
833 if (deviceService.isAvailable(device.id())) {
834 eventExecutor.submit(() -> handler.connected(device));
835 } else {
836 eventExecutor.submit(() -> handler.disconnected(device));
837 }
838 break;
839 default:
840 break;
841 }
842 }
843 }
844
845 /**
Hyunsun Moon746956f2016-01-24 21:47:06 -0800846 * Reads cordvtn nodes from config file.
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800847 */
848 private void readConfiguration() {
849 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
850
851 if (config == null) {
Hyunsun Moon746956f2016-01-24 21:47:06 -0800852 log.debug("No configuration found");
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800853 return;
854 }
855
Hyunsun Moon133fd792016-02-09 01:55:48 -0800856 NetworkAddress localMgmtIp = config.localMgmtIp();
857 TpPort ovsdbPort = config.ovsdbPort();
858 TpPort sshPort = config.sshPort();
859 String sshUser = config.sshUser();
860 String sshKeyFile = config.sshKeyFile();
861
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800862 config.cordVtnNodes().forEach(node -> {
Hyunsun Moon133fd792016-02-09 01:55:48 -0800863 log.debug("Read node {}", node.hostname());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800864 CordVtnNode cordVtnNode = new CordVtnNode(
865 node.hostname(),
Hyunsun Moon133fd792016-02-09 01:55:48 -0800866 node.hostMgmtIp(),
867 localMgmtIp,
868 node.dpIp(),
869 ovsdbPort,
870 new SshAccessInfo(node.hostMgmtIp().ip().getIp4Address(),
871 sshPort,
872 sshUser,
873 sshKeyFile),
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800874 node.bridgeId(),
Hyunsun Moon133fd792016-02-09 01:55:48 -0800875 node.dpIntf());
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800876
877 addNode(cordVtnNode);
878 });
Hyunsun Moon746956f2016-01-24 21:47:06 -0800879
880 // TODO remove nodes if needed
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800881 }
882
883 private class InternalConfigListener implements NetworkConfigListener {
884
885 @Override
886 public void event(NetworkConfigEvent event) {
887 if (!event.configClass().equals(CordVtnConfig.class)) {
888 return;
889 }
890
891 switch (event.type()) {
892 case CONFIG_ADDED:
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800893 case CONFIG_UPDATED:
Hyunsun Moonb77b60f2016-01-15 20:03:18 -0800894 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
895 break;
896 default:
897 break;
898 }
899 }
900 }
901}