blob: 954e55bdee621554e96de1a414cd16913e0b4c1a [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;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultAnnotations;
35import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Host;
38import org.onosproject.net.Port;
39import org.onosproject.net.behaviour.BridgeConfig;
40import org.onosproject.net.behaviour.BridgeName;
41import org.onosproject.net.behaviour.ControllerInfo;
42import org.onosproject.net.behaviour.DefaultTunnelDescription;
43import org.onosproject.net.behaviour.TunnelConfig;
44import org.onosproject.net.behaviour.TunnelDescription;
45import org.onosproject.net.behaviour.TunnelName;
Hyunsun Mooncb799442016-01-15 20:03:18 -080046import org.onosproject.net.config.NetworkConfigEvent;
47import org.onosproject.net.config.NetworkConfigListener;
48import org.onosproject.net.config.NetworkConfigRegistry;
49import org.onosproject.net.config.NetworkConfigService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080050import org.onosproject.net.device.DeviceAdminService;
51import org.onosproject.net.device.DeviceEvent;
52import org.onosproject.net.device.DeviceListener;
53import org.onosproject.net.device.DeviceService;
54import org.onosproject.net.driver.DriverHandler;
55import org.onosproject.net.driver.DriverService;
56import org.onosproject.net.flow.FlowRuleService;
57import org.onosproject.net.group.GroupService;
58import org.onosproject.net.host.HostService;
59import org.onosproject.ovsdb.controller.OvsdbClientService;
60import org.onosproject.ovsdb.controller.OvsdbController;
61import org.onosproject.ovsdb.controller.OvsdbNodeId;
62import org.onosproject.store.serializers.KryoNamespaces;
63import org.onosproject.store.service.ConsistentMap;
64import org.onosproject.store.service.Serializer;
65import org.onosproject.store.service.StorageService;
Hyunsun Moond05b32e2016-03-02 19:27:26 -080066import org.onosproject.store.service.Versioned;
Hyunsun Mooncb799442016-01-15 20:03:18 -080067import org.slf4j.Logger;
68
69import java.util.ArrayList;
70import java.util.HashMap;
71import java.util.List;
72import java.util.Map;
Hyunsun Moon126171d2016-02-09 01:55:48 -080073import java.util.Set;
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -080096 .register(KryoNamespaces.MISC)
Hyunsun Mooncb799442016-01-15 20:03:18 -080097 .register(CordVtnNode.class)
Hyunsun Moon126171d2016-02-09 01:55:48 -080098 .register(NodeState.class)
99 .register(SshAccessInfo.class)
100 .register(NetworkAddress.class);
Hyunsun Mooncb799442016-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 Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800177 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800187 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800188 nodeManager.connectOvsdb(node);
189 } else {
190 nodeManager.createTunnelInterface(node);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800191 nodeManager.addDataPlaneInterface(node);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800192 }
193 }
194 },
Hyunsun Moon126171d2016-02-09 01:55:48 -0800195 PORTS_ADDED {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800196 @Override
197 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800198 nodeManager.setIpAddress(node);
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800199 }
200
201 },
Hyunsun Mooncb799442016-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 Mooncb799442016-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 Mooncb799442016-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 Moon3fc17f72016-01-24 21:47:06 -0800235 readConfiguration();
Hyunsun Mooncb799442016-01-15 20:03:18 -0800236 }
237
238 @Deactivate
239 protected void deactivate() {
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800255 nodeStore.putIfAbsent(node, getNodeState(node));
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800267 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800287 NodeState state = getNodeState(node);
Hyunsun Mooncb799442016-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 Moon126171d2016-02-09 01:55:48 -0800297 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800298 checkNotNull(node);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800299 return nodeStore.containsKey(node) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800300 }
301
302 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800303 * Returns if current node state saved in nodeStore is COMPLETE or not.
304 *
305 * @param node cordvtn node
306 * @return true if it's complete state, otherwise false
307 */
308 private boolean isNodeStateComplete(CordVtnNode node) {
309 checkNotNull(node);
310
311 // the state saved in nodeStore can be wrong if IP address settings are changed
312 // after the node init has been completed since there's no way to detect it
313 // getNodeState and checkNodeInitState always return correct answer but can be slow
314 Versioned<NodeState> state = nodeStore.get(node);
315 return state != null && state.value().equals(NodeState.COMPLETE);
316 }
317
318 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800319 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800320 *
321 * @param node cordvtn node
322 * @return string including detailed node init state
323 */
324 public String checkNodeInitState(CordVtnNode node) {
325 checkNotNull(node);
326
Hyunsun Moon126171d2016-02-09 01:55:48 -0800327 if (!nodeStore.containsKey(node)) {
328 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800329 return null;
330 }
331
Hyunsun Moon126171d2016-02-09 01:55:48 -0800332 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
333 if (session == null) {
334 log.debug("Failed to SSH to {}", node.hostname());
335 return null;
336 }
337
338 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800339 String result = String.format(
340 "Integration bridge created/connected : %s (%s)%n" +
341 "VXLAN interface created : %s%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800342 "Data plane interface added : %s (%s)%n" +
343 "IP flushed from %s : %s%n" +
344 "Data plane IP added to br-int : %s (%s)%n" +
345 "Local management IP added to br-int : %s (%s)",
346 isBrIntCreated(node) ? OK : NO, DEFAULT_BRIDGE,
347 isTunnelIntfCreated(node) ? OK : NO,
348 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
349 node.dpIntf(),
350 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
351 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
352 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
353
354 RemoteIpCommandUtil.disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800355
356 return result;
357 }
358
359 /**
360 * Returns the number of the nodes known to the service.
361 *
362 * @return number of nodes
363 */
364 public int getNodeCount() {
365 return nodeStore.size();
366 }
367
368 /**
369 * Returns all nodes known to the service.
370 *
371 * @return list of nodes
372 */
373 public List<CordVtnNode> getNodes() {
374 List<CordVtnNode> nodes = new ArrayList<>();
375 nodes.addAll(nodeStore.keySet());
376 return nodes;
377 }
378
379 /**
380 * Returns cordvtn node associated with a given OVSDB device.
381 *
382 * @param ovsdbId OVSDB device id
383 * @return cordvtn node, null if it fails to find the node
384 */
385 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
386 return getNodes().stream()
387 .filter(node -> node.ovsdbId().equals(ovsdbId))
388 .findFirst().orElse(null);
389 }
390
391 /**
392 * Returns cordvtn node associated with a given integration bridge.
393 *
394 * @param bridgeId device id of integration bridge
395 * @return cordvtn node, null if it fails to find the node
396 */
397 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
398 return getNodes().stream()
399 .filter(node -> node.intBrId().equals(bridgeId))
400 .findFirst().orElse(null);
401 }
402
403 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800404 * Sets a new state for a given cordvtn node.
405 *
406 * @param node cordvtn node
407 * @param newState new node state
408 */
409 private void setNodeState(CordVtnNode node, NodeState newState) {
410 checkNotNull(node);
411
412 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
413
414 nodeStore.put(node, newState);
415 newState.process(this, node);
416 }
417
418 /**
419 * Checks current state of a given cordvtn node and returns it.
420 *
421 * @param node cordvtn node
422 * @return node state
423 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800424 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800425 checkNotNull(node);
426
Hyunsun Moon126171d2016-02-09 01:55:48 -0800427 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
428 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800429 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800430 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
431 return NodeState.PORTS_ADDED;
432 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800433 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800434 } else {
435 return NodeState.INIT;
436 }
437 }
438
439 /**
440 * Performs tasks after node initialization.
441 * It disconnects unnecessary OVSDB connection and installs initial flow
442 * rules on the device.
443 *
444 * @param node cordvtn node
445 */
446 private void postInit(CordVtnNode node) {
447 disconnectOvsdb(node);
448
Hyunsun Moon126171d2016-02-09 01:55:48 -0800449 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800450
451 // add existing hosts to the service
452 deviceService.getPorts(node.intBrId()).stream()
453 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
454 port.isEnabled())
455 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
456
457 // remove stale hosts from the service
458 hostService.getHosts().forEach(host -> {
459 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
460 if (port == null) {
461 cordVtnService.removeServiceVm(getConnectPoint(host));
462 }
463 });
464
465 log.info("Finished init {}", node.hostname());
466 }
467
468 /**
469 * Returns port name.
470 *
471 * @param port port
472 * @return port name
473 */
474 private String getPortName(Port port) {
475 return port.annotations().value("portName");
476 }
477
478 /**
479 * Returns connection state of OVSDB server for a given node.
480 *
481 * @param node cordvtn node
482 * @return true if it is connected, false otherwise
483 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800484 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800485 checkNotNull(node);
486
487 OvsdbClientService ovsdbClient = getOvsdbClient(node);
488 return deviceService.isAvailable(node.ovsdbId()) &&
489 ovsdbClient != null && ovsdbClient.isConnected();
490 }
491
492 /**
493 * Connects to OVSDB server for a given node.
494 *
495 * @param node cordvtn node
496 */
497 private void connectOvsdb(CordVtnNode node) {
498 checkNotNull(node);
499
500 if (!nodeStore.containsKey(node)) {
501 log.warn("Node {} does not exist", node.hostname());
502 return;
503 }
504
Hyunsun Moon126171d2016-02-09 01:55:48 -0800505 if (!isOvsdbConnected(node)) {
506 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800507 }
508 }
509
510 /**
511 * Disconnects OVSDB server for a given node.
512 *
513 * @param node cordvtn node
514 */
515 private void disconnectOvsdb(CordVtnNode node) {
516 checkNotNull(node);
517
518 if (!nodeStore.containsKey(node)) {
519 log.warn("Node {} does not exist", node.hostname());
520 return;
521 }
522
Hyunsun Moon126171d2016-02-09 01:55:48 -0800523 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800524 OvsdbClientService ovsdbClient = getOvsdbClient(node);
525 ovsdbClient.disconnect();
526 }
527 }
528
529 /**
530 * Returns OVSDB client for a given node.
531 *
532 * @param node cordvtn node
533 * @return OVSDB client, or null if it fails to get OVSDB client
534 */
535 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
536 checkNotNull(node);
537
538 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800539 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800540 if (ovsdbClient == null) {
541 log.trace("Couldn't find OVSDB client for {}", node.hostname());
542 }
543 return ovsdbClient;
544 }
545
546 /**
547 * Creates an integration bridge for a given node.
548 *
549 * @param node cordvtn node
550 */
551 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800552 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800553 return;
554 }
555
556 List<ControllerInfo> controllers = new ArrayList<>();
557 Sets.newHashSet(clusterService.getNodes()).stream()
558 .forEach(controller -> {
559 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
560 controllers.add(ctrlInfo);
561 });
562
563 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
564
565 try {
566 DriverHandler handler = driverService.createHandler(node.ovsdbId());
567 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
568 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
569 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800570 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800571 }
572 }
573
574 /**
575 * Creates tunnel interface to the integration bridge for a given node.
576 *
577 * @param node cordvtn node
578 */
579 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800580 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800581 return;
582 }
583
584 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
585 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
586 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
587 }
588
589 TunnelDescription description = new DefaultTunnelDescription(
590 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
591 optionBuilder.build());
592
593 try {
594 DriverHandler handler = driverService.createHandler(node.ovsdbId());
595 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
596 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
597 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800598 log.warn("Failed to create tunnel interface on {}", node.hostname());
599 }
600 }
601
602 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800603 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800604 *
605 * @param node cordvtn node
606 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800607 private void addDataPlaneInterface(CordVtnNode node) {
608 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800609 return;
610 }
611
612 try {
613 DriverHandler handler = driverService.createHandler(node.ovsdbId());
614 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800615 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800616 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800617 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
618 }
619 }
620
621 /**
622 * Flushes IP address from data plane interface and adds data plane IP address
623 * to integration bridge.
624 *
625 * @param node cordvtn node
626 */
627 private void setIpAddress(CordVtnNode node) {
628 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
629 if (session == null) {
630 log.debug("Failed to SSH to {}", node.hostname());
631 return;
632 }
633
634 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
635 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
636 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
637 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
638 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
639
640 RemoteIpCommandUtil.disconnect(session);
641
642 if (result) {
643 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800644 }
645 }
646
647 /**
648 * Checks if integration bridge exists and available.
649 *
650 * @param node cordvtn node
651 * @return true if the bridge is available, false otherwise
652 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800653 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800654 return (deviceService.getDevice(node.intBrId()) != null
655 && deviceService.isAvailable(node.intBrId()));
656 }
657
658 /**
659 * Checks if tunnel interface exists.
660 *
661 * @param node cordvtn node
662 * @return true if the interface exists, false otherwise
663 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800664 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800665 return deviceService.getPorts(node.intBrId())
666 .stream()
667 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
668 p.isEnabled())
669 .findAny().isPresent();
670 }
671
672 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800673 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800674 *
675 * @param node cordvtn node
676 * @return true if the interface exists, false otherwise
677 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800678 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800679 return deviceService.getPorts(node.intBrId())
680 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800681 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800682 p.isEnabled())
683 .findAny().isPresent();
684 }
685
686 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800687 * Checks if the IP addresses are correctly set.
688 *
689 * @param node cordvtn node
690 * @return true if the IP is set, false otherwise
691 */
692 private boolean isIpAddressSet(CordVtnNode node) {
693 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
694 if (session == null) {
695 log.debug("Failed to SSH to {}", node.hostname());
696 return false;
697 }
698
699 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
700 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
701 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
702 intBrIps.contains(node.dpIp().ip()) &&
703 intBrIps.contains(node.localMgmtIp().ip()) &&
704 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
705
706 RemoteIpCommandUtil.disconnect(session);
707 return result;
708 }
709
710 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800711 * Returns connect point of a given port.
712 *
713 * @param port port
714 * @return connect point
715 */
716 private ConnectPoint getConnectPoint(Port port) {
717 return new ConnectPoint(port.element().id(), port.number());
718 }
719
720 /**
721 * Returns connect point of a given host.
722 *
723 * @param host host
724 * @return connect point
725 */
726 private ConnectPoint getConnectPoint(Host host) {
727 return new ConnectPoint(host.location().deviceId(), host.location().port());
728 }
729
730 private class OvsdbHandler implements ConnectionHandler<Device> {
731
732 @Override
733 public void connected(Device device) {
734 CordVtnNode node = getNodeByOvsdbId(device.id());
735 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800736 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800737 } else {
738 log.debug("{} is detected on unregistered node, ignore it.", device.id());
739 }
740 }
741
742 @Override
743 public void disconnected(Device device) {
744 if (!deviceService.isAvailable(device.id())) {
745 adminService.removeDevice(device.id());
746 }
747 }
748 }
749
750 private class BridgeHandler implements ConnectionHandler<Device> {
751
752 @Override
753 public void connected(Device device) {
754 CordVtnNode node = getNodeByBridgeId(device.id());
755 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800756 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800757 } else {
758 log.debug("{} is detected on unregistered node, ignore it.", device.id());
759 }
760 }
761
762 @Override
763 public void disconnected(Device device) {
764 CordVtnNode node = getNodeByBridgeId(device.id());
765 if (node != null) {
766 log.debug("Integration Bridge is disconnected from {}", node.hostname());
767 setNodeState(node, NodeState.INCOMPLETE);
768 }
769 }
770
771 /**
772 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800773 * If the added port is tunnel or data plane interface, proceed to the remaining
774 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800775 *
776 * @param port port
777 */
778 public void portAdded(Port port) {
779 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
780 String portName = getPortName(port);
781
782 if (node == null) {
783 log.debug("{} is added to unregistered node, ignore it.", portName);
784 return;
785 }
786
787 log.debug("Port {} is added to {}", portName, node.hostname());
788
789 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800790 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800791 cordVtnService.addServiceVm(node, getConnectPoint(port));
792 } else {
793 log.debug("VM is detected on incomplete node, ignore it.", portName);
794 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800795 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
796 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800797 }
798 }
799
800 /**
801 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800802 * If the removed port is tunnel or data plane interface, proceed to the remaining
803 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800804 *
805 * @param port port
806 */
807 public void portRemoved(Port port) {
808 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
809 String portName = getPortName(port);
810
811 if (node == null) {
812 return;
813 }
814
815 log.debug("Port {} is removed from {}", 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.removeServiceVm(getConnectPoint(port));
820 } else {
821 log.debug("VM is vanished from incomplete node, ignore it.", portName);
822 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800823 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800824 setNodeState(node, NodeState.INCOMPLETE);
825 }
826 }
827 }
828
829 private class InternalDeviceListener implements DeviceListener {
830
831 @Override
832 public void event(DeviceEvent event) {
833
834 Device device = event.subject();
835 ConnectionHandler<Device> handler =
836 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
837
838 switch (event.type()) {
839 case PORT_ADDED:
840 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
841 break;
842 case PORT_UPDATED:
843 if (!event.port().isEnabled()) {
844 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
845 }
846 break;
847 case DEVICE_ADDED:
848 case DEVICE_AVAILABILITY_CHANGED:
849 if (deviceService.isAvailable(device.id())) {
850 eventExecutor.submit(() -> handler.connected(device));
851 } else {
852 eventExecutor.submit(() -> handler.disconnected(device));
853 }
854 break;
855 default:
856 break;
857 }
858 }
859 }
860
861 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800862 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800863 */
864 private void readConfiguration() {
865 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800866 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800867 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800868 return;
869 }
870
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800871 config.cordVtnNodes().forEach(this::addNode);
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800872 // TODO remove nodes if needed
Hyunsun Mooncb799442016-01-15 20:03:18 -0800873 }
874
875 private class InternalConfigListener implements NetworkConfigListener {
876
877 @Override
878 public void event(NetworkConfigEvent event) {
879 if (!event.configClass().equals(CordVtnConfig.class)) {
880 return;
881 }
882
883 switch (event.type()) {
884 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800885 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800886 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
887 break;
888 default:
889 break;
890 }
891 }
892 }
893}