blob: 34f8b9c44e0f2a39820673c802d8539d68a5f068 [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 Mooncb799442016-01-15 20:03:18 -0800236 DEFAULT_TUNNEL);
237
238 deviceService.addListener(deviceListener);
239 configService.addListener(configListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800240 }
241
242 @Deactivate
243 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800244 configService.removeListener(configListener);
245 deviceService.removeListener(deviceListener);
246
247 eventExecutor.shutdown();
248 nodeStore.clear();
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800249 leadershipService.withdraw(appId.name());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800250 }
251
252 /**
253 * Adds a new node to the service.
254 *
255 * @param node cordvtn node
256 */
257 public void addNode(CordVtnNode node) {
258 checkNotNull(node);
259
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800260 // allow update node attributes
261 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800262 initNode(node);
263 }
264
265 /**
266 * Deletes a node from the service.
267 *
268 * @param node cordvtn node
269 */
270 public void deleteNode(CordVtnNode node) {
271 checkNotNull(node);
272
Hyunsun Moon126171d2016-02-09 01:55:48 -0800273 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800274 disconnectOvsdb(node);
275 }
276
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800277 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800278 }
279
280 /**
281 * Initiates node to serve virtual tenant network.
282 *
283 * @param node cordvtn node
284 */
285 public void initNode(CordVtnNode node) {
286 checkNotNull(node);
287
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800288 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800289 log.warn("Node {} does not exist, add node first", node.hostname());
290 return;
291 }
292
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800293 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
294 log.debug("Node init requested, local: {} leader: {}", localNodeId, leaderNodeId);
295 if (!Objects.equals(localNodeId, leaderNodeId)) {
296 // only the leader performs node init
297 return;
298 }
299
Hyunsun Moon126171d2016-02-09 01:55:48 -0800300 NodeState state = getNodeState(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800301 log.debug("Init node: {} state: {}", node.hostname(), state.toString());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800302 state.process(this, node);
303 }
304
305 /**
306 * Returns node initialization state.
307 *
308 * @param node cordvtn node
309 * @return true if initial node setup is completed, otherwise false
310 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800311 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800312 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800313 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800314 }
315
316 /**
Hyunsun Mooncbc885f2016-03-08 15:37:57 -0800317 * Flush flows installed by cordvtn.
318 */
319 public void flushRules() {
320 ruleInstaller.flushRules();
321 }
322
323 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800324 * Returns if current node state saved in nodeStore is COMPLETE or not.
325 *
326 * @param node cordvtn node
327 * @return true if it's complete state, otherwise false
328 */
329 private boolean isNodeStateComplete(CordVtnNode node) {
330 checkNotNull(node);
331
332 // the state saved in nodeStore can be wrong if IP address settings are changed
333 // after the node init has been completed since there's no way to detect it
334 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800335 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
336 CordVtnNodeState state = versionedNode.value().state();
337 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800338 }
339
340 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800341 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800342 *
343 * @param node cordvtn node
344 * @return string including detailed node init state
345 */
346 public String checkNodeInitState(CordVtnNode node) {
347 checkNotNull(node);
348
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800349 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800350 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800351 return null;
352 }
353
Hyunsun Moon126171d2016-02-09 01:55:48 -0800354 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
355 if (session == null) {
356 log.debug("Failed to SSH to {}", node.hostname());
357 return null;
358 }
359
360 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800361 String result = String.format(
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800362 "br-int created and connected : %s (%s)%n" +
Hyunsun Mooncb799442016-01-15 20:03:18 -0800363 "VXLAN interface created : %s%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800364 "Data plane interface added : %s (%s)%n" +
365 "IP flushed from %s : %s%n" +
366 "Data plane IP added to br-int : %s (%s)%n" +
367 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800368 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800369 isTunnelIntfCreated(node) ? OK : NO,
370 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
371 node.dpIntf(),
372 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
373 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
374 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
375
376 RemoteIpCommandUtil.disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800377
378 return result;
379 }
380
381 /**
382 * Returns the number of the nodes known to the service.
383 *
384 * @return number of nodes
385 */
386 public int getNodeCount() {
387 return nodeStore.size();
388 }
389
390 /**
391 * Returns all nodes known to the service.
392 *
393 * @return list of nodes
394 */
395 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800396 return nodeStore.values().stream()
397 .map(Versioned::value)
398 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800399 }
400
401 /**
402 * Returns cordvtn node associated with a given OVSDB device.
403 *
404 * @param ovsdbId OVSDB device id
405 * @return cordvtn node, null if it fails to find the node
406 */
407 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
408 return getNodes().stream()
409 .filter(node -> node.ovsdbId().equals(ovsdbId))
410 .findFirst().orElse(null);
411 }
412
413 /**
414 * Returns cordvtn node associated with a given integration bridge.
415 *
416 * @param bridgeId device id of integration bridge
417 * @return cordvtn node, null if it fails to find the node
418 */
419 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
420 return getNodes().stream()
421 .filter(node -> node.intBrId().equals(bridgeId))
422 .findFirst().orElse(null);
423 }
424
425 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800426 * Sets a new state for a given cordvtn node.
427 *
428 * @param node cordvtn node
429 * @param newState new node state
430 */
431 private void setNodeState(CordVtnNode node, NodeState newState) {
432 checkNotNull(node);
433
434 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800435 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800436 newState.process(this, node);
437 }
438
439 /**
440 * Checks current state of a given cordvtn node and returns it.
441 *
442 * @param node cordvtn node
443 * @return node state
444 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800445 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800446 checkNotNull(node);
447
Hyunsun Moon126171d2016-02-09 01:55:48 -0800448 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
449 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800450 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800451 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
452 return NodeState.PORTS_ADDED;
453 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800454 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800455 } else {
456 return NodeState.INIT;
457 }
458 }
459
460 /**
461 * Performs tasks after node initialization.
462 * It disconnects unnecessary OVSDB connection and installs initial flow
463 * rules on the device.
464 *
465 * @param node cordvtn node
466 */
467 private void postInit(CordVtnNode node) {
468 disconnectOvsdb(node);
469
Hyunsun Moon126171d2016-02-09 01:55:48 -0800470 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800471
472 // add existing hosts to the service
473 deviceService.getPorts(node.intBrId()).stream()
474 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
475 port.isEnabled())
476 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
477
478 // remove stale hosts from the service
479 hostService.getHosts().forEach(host -> {
480 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
481 if (port == null) {
482 cordVtnService.removeServiceVm(getConnectPoint(host));
483 }
484 });
485
486 log.info("Finished init {}", node.hostname());
487 }
488
489 /**
490 * Returns port name.
491 *
492 * @param port port
493 * @return port name
494 */
495 private String getPortName(Port port) {
496 return port.annotations().value("portName");
497 }
498
499 /**
500 * Returns connection state of OVSDB server for a given node.
501 *
502 * @param node cordvtn node
503 * @return true if it is connected, false otherwise
504 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800505 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800506 checkNotNull(node);
507
508 OvsdbClientService ovsdbClient = getOvsdbClient(node);
509 return deviceService.isAvailable(node.ovsdbId()) &&
510 ovsdbClient != null && ovsdbClient.isConnected();
511 }
512
513 /**
514 * Connects to OVSDB server for a given node.
515 *
516 * @param node cordvtn node
517 */
518 private void connectOvsdb(CordVtnNode node) {
519 checkNotNull(node);
520
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800521 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800522 log.warn("Node {} does not exist", node.hostname());
523 return;
524 }
525
Hyunsun Moon126171d2016-02-09 01:55:48 -0800526 if (!isOvsdbConnected(node)) {
527 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800528 }
529 }
530
531 /**
532 * Disconnects OVSDB server for a given node.
533 *
534 * @param node cordvtn node
535 */
536 private void disconnectOvsdb(CordVtnNode node) {
537 checkNotNull(node);
538
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800539 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800540 log.warn("Node {} does not exist", node.hostname());
541 return;
542 }
543
Hyunsun Moon126171d2016-02-09 01:55:48 -0800544 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800545 OvsdbClientService ovsdbClient = getOvsdbClient(node);
546 ovsdbClient.disconnect();
547 }
548 }
549
550 /**
551 * Returns OVSDB client for a given node.
552 *
553 * @param node cordvtn node
554 * @return OVSDB client, or null if it fails to get OVSDB client
555 */
556 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
557 checkNotNull(node);
558
559 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800560 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800561 if (ovsdbClient == null) {
562 log.trace("Couldn't find OVSDB client for {}", node.hostname());
563 }
564 return ovsdbClient;
565 }
566
567 /**
568 * Creates an integration bridge for a given node.
569 *
570 * @param node cordvtn node
571 */
572 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800573 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800574 return;
575 }
576
577 List<ControllerInfo> controllers = new ArrayList<>();
578 Sets.newHashSet(clusterService.getNodes()).stream()
579 .forEach(controller -> {
580 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
581 controllers.add(ctrlInfo);
582 });
583
584 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
585
586 try {
587 DriverHandler handler = driverService.createHandler(node.ovsdbId());
588 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
589 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
590 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800591 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800592 }
593 }
594
595 /**
596 * Creates tunnel interface to the integration bridge for a given node.
597 *
598 * @param node cordvtn node
599 */
600 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800601 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800602 return;
603 }
604
605 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
606 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
607 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
608 }
609
610 TunnelDescription description = new DefaultTunnelDescription(
611 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
612 optionBuilder.build());
613
614 try {
615 DriverHandler handler = driverService.createHandler(node.ovsdbId());
616 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
617 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
618 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800619 log.warn("Failed to create tunnel interface on {}", node.hostname());
620 }
621 }
622
623 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800624 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800625 *
626 * @param node cordvtn node
627 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800628 private void addDataPlaneInterface(CordVtnNode node) {
629 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800630 return;
631 }
632
633 try {
634 DriverHandler handler = driverService.createHandler(node.ovsdbId());
635 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800636 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800637 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800638 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
639 }
640 }
641
642 /**
643 * Flushes IP address from data plane interface and adds data plane IP address
644 * to integration bridge.
645 *
646 * @param node cordvtn node
647 */
648 private void setIpAddress(CordVtnNode node) {
649 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
650 if (session == null) {
651 log.debug("Failed to SSH to {}", node.hostname());
652 return;
653 }
654
Hyunsun Moonf2760522016-03-04 19:24:08 -0800655 RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE).stream()
656 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
657 .filter(ip -> !ip.equals(node.dpIp().ip()))
658 .forEach(ip -> RemoteIpCommandUtil.deleteIp(session, ip, DEFAULT_BRIDGE));
659
Hyunsun Moon126171d2016-02-09 01:55:48 -0800660 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
661 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
662 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
663 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
664 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
665
666 RemoteIpCommandUtil.disconnect(session);
667
668 if (result) {
669 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800670 }
671 }
672
673 /**
674 * Checks if integration bridge exists and available.
675 *
676 * @param node cordvtn node
677 * @return true if the bridge is available, false otherwise
678 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800679 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800680 return (deviceService.getDevice(node.intBrId()) != null
681 && deviceService.isAvailable(node.intBrId()));
682 }
683
684 /**
685 * Checks if tunnel interface exists.
686 *
687 * @param node cordvtn node
688 * @return true if the interface exists, false otherwise
689 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800690 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800691 return deviceService.getPorts(node.intBrId())
692 .stream()
693 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
694 p.isEnabled())
695 .findAny().isPresent();
696 }
697
698 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800699 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800700 *
701 * @param node cordvtn node
702 * @return true if the interface exists, false otherwise
703 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800704 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800705 return deviceService.getPorts(node.intBrId())
706 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800707 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800708 p.isEnabled())
709 .findAny().isPresent();
710 }
711
712 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800713 * Checks if the IP addresses are correctly set.
714 *
715 * @param node cordvtn node
716 * @return true if the IP is set, false otherwise
717 */
718 private boolean isIpAddressSet(CordVtnNode node) {
719 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
720 if (session == null) {
721 log.debug("Failed to SSH to {}", node.hostname());
722 return false;
723 }
724
725 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
726 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
727 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
728 intBrIps.contains(node.dpIp().ip()) &&
729 intBrIps.contains(node.localMgmtIp().ip()) &&
730 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
731
732 RemoteIpCommandUtil.disconnect(session);
733 return result;
734 }
735
736 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800737 * Returns connect point of a given port.
738 *
739 * @param port port
740 * @return connect point
741 */
742 private ConnectPoint getConnectPoint(Port port) {
743 return new ConnectPoint(port.element().id(), port.number());
744 }
745
746 /**
747 * Returns connect point of a given host.
748 *
749 * @param host host
750 * @return connect point
751 */
752 private ConnectPoint getConnectPoint(Host host) {
753 return new ConnectPoint(host.location().deviceId(), host.location().port());
754 }
755
756 private class OvsdbHandler implements ConnectionHandler<Device> {
757
758 @Override
759 public void connected(Device device) {
760 CordVtnNode node = getNodeByOvsdbId(device.id());
761 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800762 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800763 } else {
764 log.debug("{} is detected on unregistered node, ignore it.", device.id());
765 }
766 }
767
768 @Override
769 public void disconnected(Device device) {
770 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800771 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800772 adminService.removeDevice(device.id());
773 }
774 }
775 }
776
777 private class BridgeHandler implements ConnectionHandler<Device> {
778
779 @Override
780 public void connected(Device device) {
781 CordVtnNode node = getNodeByBridgeId(device.id());
782 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800783 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800784 } else {
785 log.debug("{} is detected on unregistered node, ignore it.", device.id());
786 }
787 }
788
789 @Override
790 public void disconnected(Device device) {
791 CordVtnNode node = getNodeByBridgeId(device.id());
792 if (node != null) {
793 log.debug("Integration Bridge is disconnected from {}", node.hostname());
794 setNodeState(node, NodeState.INCOMPLETE);
795 }
796 }
797
798 /**
799 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800800 * If the added port is tunnel or data plane interface, proceed to the remaining
801 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800802 *
803 * @param port port
804 */
805 public void portAdded(Port port) {
806 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
807 String portName = getPortName(port);
808
809 if (node == null) {
810 log.debug("{} is added to unregistered node, ignore it.", portName);
811 return;
812 }
813
814 log.debug("Port {} is added to {}", portName, node.hostname());
815
816 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800817 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800818 cordVtnService.addServiceVm(node, getConnectPoint(port));
819 } else {
820 log.debug("VM is detected on incomplete node, ignore it.", portName);
821 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800822 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
823 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800824 }
825 }
826
827 /**
828 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800829 * If the removed port is tunnel or data plane interface, proceed to the remaining
830 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800831 *
832 * @param port port
833 */
834 public void portRemoved(Port port) {
835 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
836 String portName = getPortName(port);
837
838 if (node == null) {
839 return;
840 }
841
842 log.debug("Port {} is removed from {}", portName, node.hostname());
843
844 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800845 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800846 cordVtnService.removeServiceVm(getConnectPoint(port));
847 } else {
848 log.debug("VM is vanished from incomplete node, ignore it.", portName);
849 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800850 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800851 setNodeState(node, NodeState.INCOMPLETE);
852 }
853 }
854 }
855
856 private class InternalDeviceListener implements DeviceListener {
857
858 @Override
859 public void event(DeviceEvent event) {
860
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800861 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
862 if (!Objects.equals(localNodeId, leaderNodeId)) {
863 // only the leader processes events
864 return;
865 }
866
Hyunsun Mooncb799442016-01-15 20:03:18 -0800867 Device device = event.subject();
868 ConnectionHandler<Device> handler =
869 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
870
871 switch (event.type()) {
872 case PORT_ADDED:
873 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
874 break;
875 case PORT_UPDATED:
876 if (!event.port().isEnabled()) {
877 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
878 }
879 break;
880 case DEVICE_ADDED:
881 case DEVICE_AVAILABILITY_CHANGED:
882 if (deviceService.isAvailable(device.id())) {
883 eventExecutor.submit(() -> handler.connected(device));
884 } else {
885 eventExecutor.submit(() -> handler.disconnected(device));
886 }
887 break;
888 default:
889 break;
890 }
891 }
892 }
893
894 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800895 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800896 */
897 private void readConfiguration() {
898 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800899 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800900 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800901 return;
902 }
903
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800904 config.cordVtnNodes().forEach(this::addNode);
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800905 // TODO remove nodes if needed
Hyunsun Mooncb799442016-01-15 20:03:18 -0800906 }
907
908 private class InternalConfigListener implements NetworkConfigListener {
909
910 @Override
911 public void event(NetworkConfigEvent event) {
912 if (!event.configClass().equals(CordVtnConfig.class)) {
913 return;
914 }
915
916 switch (event.type()) {
917 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800918 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800919 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
920 break;
921 default:
922 break;
923 }
924 }
925 }
926}