blob: bcd76bc4239985fe9468e92389b4f38c5ae4f9ba [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 Moon3fc17f72016-01-24 21:47:06 -0800240 readConfiguration();
Hyunsun Mooncb799442016-01-15 20:03:18 -0800241 }
242
243 @Deactivate
244 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800245 configService.removeListener(configListener);
246 deviceService.removeListener(deviceListener);
247
248 eventExecutor.shutdown();
249 nodeStore.clear();
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800250 leadershipService.withdraw(appId.name());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800251 }
252
253 /**
254 * Adds a new node to the service.
255 *
256 * @param node cordvtn node
257 */
258 public void addNode(CordVtnNode node) {
259 checkNotNull(node);
260
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800261 // allow update node attributes
262 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800263 initNode(node);
264 }
265
266 /**
267 * Deletes a node from the service.
268 *
269 * @param node cordvtn node
270 */
271 public void deleteNode(CordVtnNode node) {
272 checkNotNull(node);
273
Hyunsun Moon126171d2016-02-09 01:55:48 -0800274 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800275 disconnectOvsdb(node);
276 }
277
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800278 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800279 }
280
281 /**
282 * Initiates node to serve virtual tenant network.
283 *
284 * @param node cordvtn node
285 */
286 public void initNode(CordVtnNode node) {
287 checkNotNull(node);
288
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800289 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800290 log.warn("Node {} does not exist, add node first", node.hostname());
291 return;
292 }
293
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800294 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
295 log.debug("Node init requested, local: {} leader: {}", localNodeId, leaderNodeId);
296 if (!Objects.equals(localNodeId, leaderNodeId)) {
297 // only the leader performs node init
298 return;
299 }
300
Hyunsun Moon126171d2016-02-09 01:55:48 -0800301 NodeState state = getNodeState(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800302 log.debug("Init node: {} state: {}", node.hostname(), state.toString());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800303 state.process(this, node);
304 }
305
306 /**
307 * Returns node initialization state.
308 *
309 * @param node cordvtn node
310 * @return true if initial node setup is completed, otherwise false
311 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800312 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800313 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800314 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800315 }
316
317 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800318 * Returns if current node state saved in nodeStore is COMPLETE or not.
319 *
320 * @param node cordvtn node
321 * @return true if it's complete state, otherwise false
322 */
323 private boolean isNodeStateComplete(CordVtnNode node) {
324 checkNotNull(node);
325
326 // the state saved in nodeStore can be wrong if IP address settings are changed
327 // after the node init has been completed since there's no way to detect it
328 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800329 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
330 CordVtnNodeState state = versionedNode.value().state();
331 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800332 }
333
334 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800335 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800336 *
337 * @param node cordvtn node
338 * @return string including detailed node init state
339 */
340 public String checkNodeInitState(CordVtnNode node) {
341 checkNotNull(node);
342
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800343 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800344 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800345 return null;
346 }
347
Hyunsun Moon126171d2016-02-09 01:55:48 -0800348 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
349 if (session == null) {
350 log.debug("Failed to SSH to {}", node.hostname());
351 return null;
352 }
353
354 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800355 String result = String.format(
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800356 "br-int created and connected : %s (%s)%n" +
Hyunsun Mooncb799442016-01-15 20:03:18 -0800357 "VXLAN interface created : %s%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800358 "Data plane interface added : %s (%s)%n" +
359 "IP flushed from %s : %s%n" +
360 "Data plane IP added to br-int : %s (%s)%n" +
361 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800362 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800363 isTunnelIntfCreated(node) ? OK : NO,
364 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
365 node.dpIntf(),
366 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
367 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
368 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
369
370 RemoteIpCommandUtil.disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800371
372 return result;
373 }
374
375 /**
376 * Returns the number of the nodes known to the service.
377 *
378 * @return number of nodes
379 */
380 public int getNodeCount() {
381 return nodeStore.size();
382 }
383
384 /**
385 * Returns all nodes known to the service.
386 *
387 * @return list of nodes
388 */
389 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800390 return nodeStore.values().stream()
391 .map(Versioned::value)
392 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800393 }
394
395 /**
396 * Returns cordvtn node associated with a given OVSDB device.
397 *
398 * @param ovsdbId OVSDB device id
399 * @return cordvtn node, null if it fails to find the node
400 */
401 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
402 return getNodes().stream()
403 .filter(node -> node.ovsdbId().equals(ovsdbId))
404 .findFirst().orElse(null);
405 }
406
407 /**
408 * Returns cordvtn node associated with a given integration bridge.
409 *
410 * @param bridgeId device id of integration bridge
411 * @return cordvtn node, null if it fails to find the node
412 */
413 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
414 return getNodes().stream()
415 .filter(node -> node.intBrId().equals(bridgeId))
416 .findFirst().orElse(null);
417 }
418
419 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800420 * Sets a new state for a given cordvtn node.
421 *
422 * @param node cordvtn node
423 * @param newState new node state
424 */
425 private void setNodeState(CordVtnNode node, NodeState newState) {
426 checkNotNull(node);
427
428 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800429 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800430 newState.process(this, node);
431 }
432
433 /**
434 * Checks current state of a given cordvtn node and returns it.
435 *
436 * @param node cordvtn node
437 * @return node state
438 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800439 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800440 checkNotNull(node);
441
Hyunsun Moon126171d2016-02-09 01:55:48 -0800442 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
443 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800444 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800445 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
446 return NodeState.PORTS_ADDED;
447 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800448 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800449 } else {
450 return NodeState.INIT;
451 }
452 }
453
454 /**
455 * Performs tasks after node initialization.
456 * It disconnects unnecessary OVSDB connection and installs initial flow
457 * rules on the device.
458 *
459 * @param node cordvtn node
460 */
461 private void postInit(CordVtnNode node) {
462 disconnectOvsdb(node);
463
Hyunsun Moon126171d2016-02-09 01:55:48 -0800464 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800465
466 // add existing hosts to the service
467 deviceService.getPorts(node.intBrId()).stream()
468 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
469 port.isEnabled())
470 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
471
472 // remove stale hosts from the service
473 hostService.getHosts().forEach(host -> {
474 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
475 if (port == null) {
476 cordVtnService.removeServiceVm(getConnectPoint(host));
477 }
478 });
479
480 log.info("Finished init {}", node.hostname());
481 }
482
483 /**
484 * Returns port name.
485 *
486 * @param port port
487 * @return port name
488 */
489 private String getPortName(Port port) {
490 return port.annotations().value("portName");
491 }
492
493 /**
494 * Returns connection state of OVSDB server for a given node.
495 *
496 * @param node cordvtn node
497 * @return true if it is connected, false otherwise
498 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800499 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800500 checkNotNull(node);
501
502 OvsdbClientService ovsdbClient = getOvsdbClient(node);
503 return deviceService.isAvailable(node.ovsdbId()) &&
504 ovsdbClient != null && ovsdbClient.isConnected();
505 }
506
507 /**
508 * Connects to OVSDB server for a given node.
509 *
510 * @param node cordvtn node
511 */
512 private void connectOvsdb(CordVtnNode node) {
513 checkNotNull(node);
514
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800515 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800516 log.warn("Node {} does not exist", node.hostname());
517 return;
518 }
519
Hyunsun Moon126171d2016-02-09 01:55:48 -0800520 if (!isOvsdbConnected(node)) {
521 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800522 }
523 }
524
525 /**
526 * Disconnects OVSDB server for a given node.
527 *
528 * @param node cordvtn node
529 */
530 private void disconnectOvsdb(CordVtnNode node) {
531 checkNotNull(node);
532
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800533 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800534 log.warn("Node {} does not exist", node.hostname());
535 return;
536 }
537
Hyunsun Moon126171d2016-02-09 01:55:48 -0800538 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800539 OvsdbClientService ovsdbClient = getOvsdbClient(node);
540 ovsdbClient.disconnect();
541 }
542 }
543
544 /**
545 * Returns OVSDB client for a given node.
546 *
547 * @param node cordvtn node
548 * @return OVSDB client, or null if it fails to get OVSDB client
549 */
550 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
551 checkNotNull(node);
552
553 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800554 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800555 if (ovsdbClient == null) {
556 log.trace("Couldn't find OVSDB client for {}", node.hostname());
557 }
558 return ovsdbClient;
559 }
560
561 /**
562 * Creates an integration bridge for a given node.
563 *
564 * @param node cordvtn node
565 */
566 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800567 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800568 return;
569 }
570
571 List<ControllerInfo> controllers = new ArrayList<>();
572 Sets.newHashSet(clusterService.getNodes()).stream()
573 .forEach(controller -> {
574 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
575 controllers.add(ctrlInfo);
576 });
577
578 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
579
580 try {
581 DriverHandler handler = driverService.createHandler(node.ovsdbId());
582 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
583 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
584 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800585 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800586 }
587 }
588
589 /**
590 * Creates tunnel interface to the integration bridge for a given node.
591 *
592 * @param node cordvtn node
593 */
594 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800595 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800596 return;
597 }
598
599 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
600 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
601 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
602 }
603
604 TunnelDescription description = new DefaultTunnelDescription(
605 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
606 optionBuilder.build());
607
608 try {
609 DriverHandler handler = driverService.createHandler(node.ovsdbId());
610 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
611 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
612 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800613 log.warn("Failed to create tunnel interface on {}", node.hostname());
614 }
615 }
616
617 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800618 * Adds data plane interface to a given node.
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800619 *
620 * @param node cordvtn node
621 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800622 private void addDataPlaneInterface(CordVtnNode node) {
623 if (isDataPlaneIntfAdded(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800624 return;
625 }
626
627 try {
628 DriverHandler handler = driverService.createHandler(node.ovsdbId());
629 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800630 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800631 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800632 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
633 }
634 }
635
636 /**
637 * Flushes IP address from data plane interface and adds data plane IP address
638 * to integration bridge.
639 *
640 * @param node cordvtn node
641 */
642 private void setIpAddress(CordVtnNode node) {
643 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
644 if (session == null) {
645 log.debug("Failed to SSH to {}", node.hostname());
646 return;
647 }
648
Hyunsun Moonf2760522016-03-04 19:24:08 -0800649 RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE).stream()
650 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
651 .filter(ip -> !ip.equals(node.dpIp().ip()))
652 .forEach(ip -> RemoteIpCommandUtil.deleteIp(session, ip, DEFAULT_BRIDGE));
653
Hyunsun Moon126171d2016-02-09 01:55:48 -0800654 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
655 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
656 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
657 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
658 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
659
660 RemoteIpCommandUtil.disconnect(session);
661
662 if (result) {
663 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800664 }
665 }
666
667 /**
668 * Checks if integration bridge exists and available.
669 *
670 * @param node cordvtn node
671 * @return true if the bridge is available, false otherwise
672 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800673 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800674 return (deviceService.getDevice(node.intBrId()) != null
675 && deviceService.isAvailable(node.intBrId()));
676 }
677
678 /**
679 * Checks if tunnel interface exists.
680 *
681 * @param node cordvtn node
682 * @return true if the interface exists, false otherwise
683 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800684 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800685 return deviceService.getPorts(node.intBrId())
686 .stream()
687 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
688 p.isEnabled())
689 .findAny().isPresent();
690 }
691
692 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800693 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800694 *
695 * @param node cordvtn node
696 * @return true if the interface exists, false otherwise
697 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800698 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800699 return deviceService.getPorts(node.intBrId())
700 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800701 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800702 p.isEnabled())
703 .findAny().isPresent();
704 }
705
706 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800707 * Checks if the IP addresses are correctly set.
708 *
709 * @param node cordvtn node
710 * @return true if the IP is set, false otherwise
711 */
712 private boolean isIpAddressSet(CordVtnNode node) {
713 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
714 if (session == null) {
715 log.debug("Failed to SSH to {}", node.hostname());
716 return false;
717 }
718
719 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
720 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
721 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
722 intBrIps.contains(node.dpIp().ip()) &&
723 intBrIps.contains(node.localMgmtIp().ip()) &&
724 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
725
726 RemoteIpCommandUtil.disconnect(session);
727 return result;
728 }
729
730 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800731 * Returns connect point of a given port.
732 *
733 * @param port port
734 * @return connect point
735 */
736 private ConnectPoint getConnectPoint(Port port) {
737 return new ConnectPoint(port.element().id(), port.number());
738 }
739
740 /**
741 * Returns connect point of a given host.
742 *
743 * @param host host
744 * @return connect point
745 */
746 private ConnectPoint getConnectPoint(Host host) {
747 return new ConnectPoint(host.location().deviceId(), host.location().port());
748 }
749
750 private class OvsdbHandler implements ConnectionHandler<Device> {
751
752 @Override
753 public void connected(Device device) {
754 CordVtnNode node = getNodeByOvsdbId(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 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800765 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800766 adminService.removeDevice(device.id());
767 }
768 }
769 }
770
771 private class BridgeHandler implements ConnectionHandler<Device> {
772
773 @Override
774 public void connected(Device device) {
775 CordVtnNode node = getNodeByBridgeId(device.id());
776 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800777 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800778 } else {
779 log.debug("{} is detected on unregistered node, ignore it.", device.id());
780 }
781 }
782
783 @Override
784 public void disconnected(Device device) {
785 CordVtnNode node = getNodeByBridgeId(device.id());
786 if (node != null) {
787 log.debug("Integration Bridge is disconnected from {}", node.hostname());
788 setNodeState(node, NodeState.INCOMPLETE);
789 }
790 }
791
792 /**
793 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800794 * If the added port is tunnel or data plane interface, proceed to the remaining
795 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800796 *
797 * @param port port
798 */
799 public void portAdded(Port port) {
800 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
801 String portName = getPortName(port);
802
803 if (node == null) {
804 log.debug("{} is added to unregistered node, ignore it.", portName);
805 return;
806 }
807
808 log.debug("Port {} is added to {}", portName, node.hostname());
809
810 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800811 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800812 cordVtnService.addServiceVm(node, getConnectPoint(port));
813 } else {
814 log.debug("VM is detected on incomplete node, ignore it.", portName);
815 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800816 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
817 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800818 }
819 }
820
821 /**
822 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800823 * If the removed port is tunnel or data plane interface, proceed to the remaining
824 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800825 *
826 * @param port port
827 */
828 public void portRemoved(Port port) {
829 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
830 String portName = getPortName(port);
831
832 if (node == null) {
833 return;
834 }
835
836 log.debug("Port {} is removed from {}", portName, node.hostname());
837
838 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800839 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800840 cordVtnService.removeServiceVm(getConnectPoint(port));
841 } else {
842 log.debug("VM is vanished from incomplete node, ignore it.", portName);
843 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800844 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800845 setNodeState(node, NodeState.INCOMPLETE);
846 }
847 }
848 }
849
850 private class InternalDeviceListener implements DeviceListener {
851
852 @Override
853 public void event(DeviceEvent event) {
854
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800855 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
856 if (!Objects.equals(localNodeId, leaderNodeId)) {
857 // only the leader processes events
858 return;
859 }
860
Hyunsun Mooncb799442016-01-15 20:03:18 -0800861 Device device = event.subject();
862 ConnectionHandler<Device> handler =
863 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
864
865 switch (event.type()) {
866 case PORT_ADDED:
867 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
868 break;
869 case PORT_UPDATED:
870 if (!event.port().isEnabled()) {
871 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
872 }
873 break;
874 case DEVICE_ADDED:
875 case DEVICE_AVAILABILITY_CHANGED:
876 if (deviceService.isAvailable(device.id())) {
877 eventExecutor.submit(() -> handler.connected(device));
878 } else {
879 eventExecutor.submit(() -> handler.disconnected(device));
880 }
881 break;
882 default:
883 break;
884 }
885 }
886 }
887
888 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800889 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800890 */
891 private void readConfiguration() {
892 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800893 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800894 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800895 return;
896 }
897
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800898 config.cordVtnNodes().forEach(this::addNode);
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800899 // TODO remove nodes if needed
Hyunsun Mooncb799442016-01-15 20:03:18 -0800900 }
901
902 private class InternalConfigListener implements NetworkConfigListener {
903
904 @Override
905 public void event(NetworkConfigEvent event) {
906 if (!event.configClass().equals(CordVtnConfig.class)) {
907 return;
908 }
909
910 switch (event.type()) {
911 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800912 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800913 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
914 break;
915 default:
916 break;
917 }
918 }
919 }
920}