blob: 0b00979b22379212b38bd66fbfb1cbdd8e8586d9 [file] [log] [blame]
Hyunsun Mooncb799442016-01-15 20:03:18 -08001/*
Brian O'Connor8e57fd52016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Hyunsun Mooncb799442016-01-15 20:03:18 -08003 *
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 */
Hyunsun Moonf9480202016-04-14 16:13:42 -070016package org.onosproject.cordvtn.impl;
Hyunsun Mooncb799442016-01-15 20:03:18 -080017
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 Moonf9480202016-04-14 16:13:42 -070032import org.onosproject.cordvtn.api.ConnectionHandler;
33import org.onosproject.cordvtn.api.CordVtnConfig;
34import org.onosproject.cordvtn.api.CordVtnNode;
35import org.onosproject.cordvtn.api.CordVtnNodeState;
36import org.onosproject.cordvtn.api.CordVtnService;
37import org.onosproject.cordvtn.api.NetworkAddress;
38import org.onosproject.cordvtn.api.SshAccessInfo;
Hyunsun Mooncb799442016-01-15 20:03:18 -080039import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080041import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.DefaultAnnotations;
43import org.onosproject.net.Device;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Host;
46import org.onosproject.net.Port;
47import org.onosproject.net.behaviour.BridgeConfig;
48import org.onosproject.net.behaviour.BridgeName;
49import org.onosproject.net.behaviour.ControllerInfo;
50import org.onosproject.net.behaviour.DefaultTunnelDescription;
51import org.onosproject.net.behaviour.TunnelConfig;
52import org.onosproject.net.behaviour.TunnelDescription;
53import org.onosproject.net.behaviour.TunnelName;
Hyunsun Mooncb799442016-01-15 20:03:18 -080054import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
57import org.onosproject.net.config.NetworkConfigService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080058import org.onosproject.net.device.DeviceAdminService;
59import org.onosproject.net.device.DeviceEvent;
60import org.onosproject.net.device.DeviceListener;
61import org.onosproject.net.device.DeviceService;
Hyunsun Mooncb799442016-01-15 20:03:18 -080062import org.onosproject.net.flow.FlowRuleService;
63import org.onosproject.net.group.GroupService;
64import org.onosproject.net.host.HostService;
65import org.onosproject.ovsdb.controller.OvsdbClientService;
66import org.onosproject.ovsdb.controller.OvsdbController;
67import org.onosproject.ovsdb.controller.OvsdbNodeId;
68import org.onosproject.store.serializers.KryoNamespaces;
69import org.onosproject.store.service.ConsistentMap;
Hyunsun Moonff55e812016-03-10 12:40:16 -080070import org.onosproject.store.service.MapEvent;
71import org.onosproject.store.service.MapEventListener;
Hyunsun Mooncb799442016-01-15 20:03:18 -080072import org.onosproject.store.service.Serializer;
73import org.onosproject.store.service.StorageService;
Hyunsun Moond05b32e2016-03-02 19:27:26 -080074import org.onosproject.store.service.Versioned;
Hyunsun Mooncb799442016-01-15 20:03:18 -080075import org.slf4j.Logger;
76
77import java.util.ArrayList;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -080081import java.util.Objects;
Hyunsun Moon126171d2016-02-09 01:55:48 -080082import java.util.Set;
Hyunsun Mooncb799442016-01-15 20:03:18 -080083import java.util.concurrent.ExecutorService;
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -080084import java.util.stream.Collectors;
Hyunsun Mooncb799442016-01-15 20:03:18 -080085
86import static com.google.common.base.Preconditions.checkNotNull;
87import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
88import static org.onlab.util.Tools.groupedThreads;
89import static org.onosproject.net.Device.Type.SWITCH;
90import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
91import static org.slf4j.LoggerFactory.getLogger;
92
93/**
94 * Reads node information from the network config file and handles the config
95 * update events.
96 * Only a leader controller performs the node addition or deletion.
97 */
98@Component(immediate = true)
99@Service(value = CordVtnNodeManager.class)
100public class CordVtnNodeManager {
101
102 protected final Logger log = getLogger(getClass());
103
104 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
105 .register(KryoNamespaces.API)
106 .register(CordVtnNode.class)
Hyunsun Moon126171d2016-02-09 01:55:48 -0800107 .register(NodeState.class)
108 .register(SshAccessInfo.class)
109 .register(NetworkAddress.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800110
111 private static final String DEFAULT_BRIDGE = "br-int";
112 private static final String DEFAULT_TUNNEL = "vxlan";
113 private static final String VPORT_PREFIX = "tap";
114 private static final String OK = "OK";
115 private static final String NO = "NO";
116
117 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
118 {
119 put("key", "flow");
120 put("remote_ip", "flow");
121 }
122 };
123 private static final int DPID_BEGIN = 3;
124 private static final int OFPORT = 6653;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected CoreService coreService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected NetworkConfigRegistry configRegistry;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected NetworkConfigService configService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected StorageService storageService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected DeviceAdminService adminService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected OvsdbController controller;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected ClusterService clusterService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Mooncb799442016-01-15 20:03:18 -0800148 protected DeviceService deviceService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected HostService hostService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected FlowRuleService flowRuleService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800157 protected LeadershipService leadershipService;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected GroupService groupService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected CordVtnService cordVtnService;
164
Hyunsun Mooncb799442016-01-15 20:03:18 -0800165 private final ExecutorService eventExecutor =
166 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler"));
167
168 private final NetworkConfigListener configListener = new InternalConfigListener();
169 private final DeviceListener deviceListener = new InternalDeviceListener();
Hyunsun Moonff55e812016-03-10 12:40:16 -0800170 private final MapEventListener<String, CordVtnNode> nodeStoreListener = new InternalMapListener();
Hyunsun Mooncb799442016-01-15 20:03:18 -0800171
172 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
173 private final BridgeHandler bridgeHandler = new BridgeHandler();
174
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800175 private ConsistentMap<String, CordVtnNode> nodeStore;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800176 private CordVtnRuleInstaller ruleInstaller;
177 private ApplicationId appId;
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800178 private NodeId localNodeId;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800179
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800180 private enum NodeState implements CordVtnNodeState {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800181
182 INIT {
183 @Override
184 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800185 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800186 nodeManager.connectOvsdb(node);
187 } else {
188 nodeManager.createIntegrationBridge(node);
189 }
190 }
191 },
192 BRIDGE_CREATED {
193 @Override
194 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800195 if (!nodeManager.isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800196 nodeManager.connectOvsdb(node);
197 } else {
198 nodeManager.createTunnelInterface(node);
Hyunsun Moon126171d2016-02-09 01:55:48 -0800199 nodeManager.addDataPlaneInterface(node);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800200 }
201 }
202 },
Hyunsun Moon126171d2016-02-09 01:55:48 -0800203 PORTS_ADDED {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800204 @Override
205 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800206 nodeManager.setIpAddress(node);
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800207 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800208 },
Hyunsun Mooncb799442016-01-15 20:03:18 -0800209 COMPLETE {
210 @Override
211 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
212 nodeManager.postInit(node);
213 }
214 },
215 INCOMPLETE {
216 @Override
217 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
218 }
219 };
220
Hyunsun Mooncb799442016-01-15 20:03:18 -0800221 public abstract void process(CordVtnNodeManager nodeManager, CordVtnNode node);
222 }
223
224 @Activate
225 protected void active() {
226 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800227 localNodeId = clusterService.getLocalNode().id();
228 leadershipService.runForLeadership(appId.name());
229
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800230 nodeStore = storageService.<String, CordVtnNode>consistentMapBuilder()
Hyunsun Mooncb799442016-01-15 20:03:18 -0800231 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
232 .withName("cordvtn-nodestore")
233 .withApplicationId(appId)
234 .build();
235
236 ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
237 deviceService,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800238 groupService,
Hyunsun Moon7a9bb122016-03-08 18:07:52 -0800239 configRegistry,
Hyunsun Mooncb799442016-01-15 20:03:18 -0800240 DEFAULT_TUNNEL);
241
Hyunsun Moonff55e812016-03-10 12:40:16 -0800242 nodeStore.addListener(nodeStoreListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800243 deviceService.addListener(deviceListener);
244 configService.addListener(configListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800245 }
246
247 @Deactivate
248 protected void deactivate() {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800249 configService.removeListener(configListener);
250 deviceService.removeListener(deviceListener);
251
Hyunsun Moonff55e812016-03-10 12:40:16 -0800252 nodeStore.removeListener(nodeStoreListener);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800253 nodeStore.clear();
Hyunsun Moonff55e812016-03-10 12:40:16 -0800254
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800255 leadershipService.withdraw(appId.name());
Hyunsun Moonff55e812016-03-10 12:40:16 -0800256 eventExecutor.shutdown();
Hyunsun Mooncb799442016-01-15 20:03:18 -0800257 }
258
259 /**
Hyunsun Moonff55e812016-03-10 12:40:16 -0800260 * Adds or updates a new node to the service.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800261 *
262 * @param node cordvtn node
263 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800264 public void addOrUpdateNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800265 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800266 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800267 }
268
269 /**
270 * Deletes a node from the service.
271 *
272 * @param node cordvtn node
273 */
274 public void deleteNode(CordVtnNode node) {
275 checkNotNull(node);
276
Hyunsun Moon126171d2016-02-09 01:55:48 -0800277 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800278 disconnectOvsdb(node);
279 }
280
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800281 nodeStore.remove(node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800282 }
283
284 /**
285 * Initiates node to serve virtual tenant network.
286 *
287 * @param node cordvtn node
288 */
Hyunsun Moonff55e812016-03-10 12:40:16 -0800289 private void initNode(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800290 checkNotNull(node);
291
Hyunsun Moonff55e812016-03-10 12:40:16 -0800292 NodeState state = (NodeState) node.state();
293 log.debug("Processing node: {} state: {}", node.hostname(), state);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800294
Hyunsun Mooncb799442016-01-15 20:03:18 -0800295 state.process(this, node);
296 }
297
298 /**
299 * Returns node initialization state.
300 *
301 * @param node cordvtn node
302 * @return true if initial node setup is completed, otherwise false
303 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800304 public boolean isNodeInitComplete(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800305 checkNotNull(node);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800306 return nodeStore.containsKey(node.hostname()) && getNodeState(node).equals(NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800307 }
308
309 /**
Hyunsun Mooncbc885f2016-03-08 15:37:57 -0800310 * Flush flows installed by cordvtn.
311 */
312 public void flushRules() {
313 ruleInstaller.flushRules();
314 }
315
316 /**
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800317 * Returns if current node state saved in nodeStore is COMPLETE or not.
318 *
319 * @param node cordvtn node
320 * @return true if it's complete state, otherwise false
321 */
322 private boolean isNodeStateComplete(CordVtnNode node) {
323 checkNotNull(node);
324
325 // the state saved in nodeStore can be wrong if IP address settings are changed
326 // after the node init has been completed since there's no way to detect it
327 // getNodeState and checkNodeInitState always return correct answer but can be slow
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800328 Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
329 CordVtnNodeState state = versionedNode.value().state();
330 return state != null && state.equals(NodeState.COMPLETE);
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800331 }
332
333 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800334 * Returns detailed node initialization state.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800335 *
336 * @param node cordvtn node
337 * @return string including detailed node init state
338 */
339 public String checkNodeInitState(CordVtnNode node) {
340 checkNotNull(node);
341
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800342 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800343 log.warn("Node {} does not exist, add node first", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800344 return null;
345 }
346
Hyunsun Moon126171d2016-02-09 01:55:48 -0800347 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
348 if (session == null) {
349 log.debug("Failed to SSH to {}", node.hostname());
350 return null;
351 }
352
353 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800354 String result = String.format(
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800355 "br-int created and connected : %s (%s)%n" +
Hyunsun Mooncb799442016-01-15 20:03:18 -0800356 "VXLAN interface created : %s%n" +
Hyunsun Moon126171d2016-02-09 01:55:48 -0800357 "Data plane interface added : %s (%s)%n" +
358 "IP flushed from %s : %s%n" +
359 "Data plane IP added to br-int : %s (%s)%n" +
360 "Local management IP added to br-int : %s (%s)",
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800361 isBrIntCreated(node) ? OK : NO, node.intBrId(),
Hyunsun Moon126171d2016-02-09 01:55:48 -0800362 isTunnelIntfCreated(node) ? OK : NO,
363 isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
364 node.dpIntf(),
365 RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO,
366 intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
367 intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
368
369 RemoteIpCommandUtil.disconnect(session);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800370
371 return result;
372 }
373
374 /**
375 * Returns the number of the nodes known to the service.
376 *
377 * @return number of nodes
378 */
379 public int getNodeCount() {
380 return nodeStore.size();
381 }
382
383 /**
384 * Returns all nodes known to the service.
385 *
386 * @return list of nodes
387 */
388 public List<CordVtnNode> getNodes() {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800389 return nodeStore.values().stream()
390 .map(Versioned::value)
391 .collect(Collectors.toList());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800392 }
393
394 /**
395 * Returns cordvtn node associated with a given OVSDB device.
396 *
397 * @param ovsdbId OVSDB device id
398 * @return cordvtn node, null if it fails to find the node
399 */
400 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
401 return getNodes().stream()
402 .filter(node -> node.ovsdbId().equals(ovsdbId))
403 .findFirst().orElse(null);
404 }
405
406 /**
407 * Returns cordvtn node associated with a given integration bridge.
408 *
409 * @param bridgeId device id of integration bridge
410 * @return cordvtn node, null if it fails to find the node
411 */
412 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
413 return getNodes().stream()
414 .filter(node -> node.intBrId().equals(bridgeId))
415 .findFirst().orElse(null);
416 }
417
418 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800419 * Sets a new state for a given cordvtn node.
420 *
421 * @param node cordvtn node
422 * @param newState new node state
423 */
424 private void setNodeState(CordVtnNode node, NodeState newState) {
425 checkNotNull(node);
426
Hyunsun Moonff55e812016-03-10 12:40:16 -0800427 log.debug("Changed {} state: {}", node.hostname(), newState);
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800428 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800429 }
430
431 /**
432 * Checks current state of a given cordvtn node and returns it.
433 *
434 * @param node cordvtn node
435 * @return node state
436 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800437 private NodeState getNodeState(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800438 checkNotNull(node);
439
Hyunsun Moon126171d2016-02-09 01:55:48 -0800440 if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
441 isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800442 return NodeState.COMPLETE;
Hyunsun Moon126171d2016-02-09 01:55:48 -0800443 } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
444 return NodeState.PORTS_ADDED;
445 } else if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800446 return NodeState.BRIDGE_CREATED;
Hyunsun Mooncb799442016-01-15 20:03:18 -0800447 } else {
448 return NodeState.INIT;
449 }
450 }
451
452 /**
453 * Performs tasks after node initialization.
454 * It disconnects unnecessary OVSDB connection and installs initial flow
455 * rules on the device.
456 *
457 * @param node cordvtn node
458 */
459 private void postInit(CordVtnNode node) {
460 disconnectOvsdb(node);
461
Hyunsun Moon126171d2016-02-09 01:55:48 -0800462 ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800463
464 // add existing hosts to the service
465 deviceService.getPorts(node.intBrId()).stream()
466 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
467 port.isEnabled())
468 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
469
470 // remove stale hosts from the service
471 hostService.getHosts().forEach(host -> {
472 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
473 if (port == null) {
474 cordVtnService.removeServiceVm(getConnectPoint(host));
475 }
476 });
477
478 log.info("Finished init {}", node.hostname());
479 }
480
481 /**
482 * Returns port name.
483 *
484 * @param port port
485 * @return port name
486 */
487 private String getPortName(Port port) {
488 return port.annotations().value("portName");
489 }
490
491 /**
492 * Returns connection state of OVSDB server for a given node.
493 *
494 * @param node cordvtn node
495 * @return true if it is connected, false otherwise
496 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800497 private boolean isOvsdbConnected(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800498 checkNotNull(node);
499
500 OvsdbClientService ovsdbClient = getOvsdbClient(node);
501 return deviceService.isAvailable(node.ovsdbId()) &&
502 ovsdbClient != null && ovsdbClient.isConnected();
503 }
504
505 /**
506 * Connects to OVSDB server for a given node.
507 *
508 * @param node cordvtn node
509 */
510 private void connectOvsdb(CordVtnNode node) {
511 checkNotNull(node);
512
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800513 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800514 log.warn("Node {} does not exist", node.hostname());
515 return;
516 }
517
Hyunsun Moon126171d2016-02-09 01:55:48 -0800518 if (!isOvsdbConnected(node)) {
519 controller.connect(node.hostMgmtIp().ip(), node.ovsdbPort());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800520 }
521 }
522
523 /**
524 * Disconnects OVSDB server for a given node.
525 *
526 * @param node cordvtn node
527 */
528 private void disconnectOvsdb(CordVtnNode node) {
529 checkNotNull(node);
530
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800531 if (!nodeStore.containsKey(node.hostname())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800532 log.warn("Node {} does not exist", node.hostname());
533 return;
534 }
535
Hyunsun Moon126171d2016-02-09 01:55:48 -0800536 if (isOvsdbConnected(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800537 OvsdbClientService ovsdbClient = getOvsdbClient(node);
538 ovsdbClient.disconnect();
539 }
540 }
541
542 /**
543 * Returns OVSDB client for a given node.
544 *
545 * @param node cordvtn node
546 * @return OVSDB client, or null if it fails to get OVSDB client
547 */
548 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
549 checkNotNull(node);
550
551 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon126171d2016-02-09 01:55:48 -0800552 new OvsdbNodeId(node.hostMgmtIp().ip(), node.ovsdbPort().toInt()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800553 if (ovsdbClient == null) {
554 log.trace("Couldn't find OVSDB client for {}", node.hostname());
555 }
556 return ovsdbClient;
557 }
558
559 /**
560 * Creates an integration bridge for a given node.
561 *
562 * @param node cordvtn node
563 */
564 private void createIntegrationBridge(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800565 if (isBrIntCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800566 return;
567 }
568
569 List<ControllerInfo> controllers = new ArrayList<>();
570 Sets.newHashSet(clusterService.getNodes()).stream()
571 .forEach(controller -> {
572 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
573 controllers.add(ctrlInfo);
574 });
575
576 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
577
578 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700579 Device device = deviceService.getDevice(node.ovsdbId());
580 if (device.is(BridgeConfig.class)) {
581 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
582 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
583 } else {
584 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
585 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800586 } catch (ItemNotFoundException e) {
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800587 log.warn("Failed to create integration bridge on {}", node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800588 }
589 }
590
591 /**
592 * Creates tunnel interface to the integration bridge for a given node.
593 *
594 * @param node cordvtn node
595 */
596 private void createTunnelInterface(CordVtnNode node) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800597 if (isTunnelIntfCreated(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800598 return;
599 }
600
601 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
602 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
603 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
604 }
605
606 TunnelDescription description = new DefaultTunnelDescription(
607 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
608 optionBuilder.build());
609
610 try {
Jian Lie676a9a2016-04-15 13:22:05 -0700611 Device device = deviceService.getDevice(node.ovsdbId());
612 if (device.is(TunnelConfig.class)) {
613 TunnelConfig tunnelConfig = device.as(TunnelConfig.class);
614 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
615 } else {
616 log.warn("The tunneling behaviour is not supported in device {}", device.id().toString());
617 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800618 } 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 {
Jian Lie676a9a2016-04-15 13:22:05 -0700634 Device device = deviceService.getDevice(node.ovsdbId());
635 if (device.is(BridgeConfig.class)) {
636 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
637 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
638 } else {
639 log.warn("The bridging behaviour is not supported in device {}", device.id().toString());
640 }
Hyunsun Moonbd572c12016-01-21 00:54:52 -0800641 } catch (ItemNotFoundException e) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800642 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
643 }
644 }
645
646 /**
647 * Flushes IP address from data plane interface and adds data plane IP address
648 * to integration bridge.
649 *
650 * @param node cordvtn node
651 */
652 private void setIpAddress(CordVtnNode node) {
653 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
654 if (session == null) {
655 log.debug("Failed to SSH to {}", node.hostname());
656 return;
657 }
658
Hyunsun Moonf2760522016-03-04 19:24:08 -0800659 RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE).stream()
660 .filter(ip -> !ip.equals(node.localMgmtIp().ip()))
661 .filter(ip -> !ip.equals(node.dpIp().ip()))
662 .forEach(ip -> RemoteIpCommandUtil.deleteIp(session, ip, DEFAULT_BRIDGE));
663
Hyunsun Moon126171d2016-02-09 01:55:48 -0800664 boolean result = RemoteIpCommandUtil.flushIp(session, node.dpIntf()) &&
665 RemoteIpCommandUtil.setInterfaceUp(session, node.dpIntf()) &&
666 RemoteIpCommandUtil.addIp(session, node.dpIp(), DEFAULT_BRIDGE) &&
667 RemoteIpCommandUtil.addIp(session, node.localMgmtIp(), DEFAULT_BRIDGE) &&
668 RemoteIpCommandUtil.setInterfaceUp(session, DEFAULT_BRIDGE);
669
670 RemoteIpCommandUtil.disconnect(session);
671
672 if (result) {
673 setNodeState(node, NodeState.COMPLETE);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800674 }
675 }
676
677 /**
678 * Checks if integration bridge exists and available.
679 *
680 * @param node cordvtn node
681 * @return true if the bridge is available, false otherwise
682 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800683 private boolean isBrIntCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800684 return (deviceService.getDevice(node.intBrId()) != null
685 && deviceService.isAvailable(node.intBrId()));
686 }
687
688 /**
689 * Checks if tunnel interface exists.
690 *
691 * @param node cordvtn node
692 * @return true if the interface exists, false otherwise
693 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800694 private boolean isTunnelIntfCreated(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800695 return deviceService.getPorts(node.intBrId())
696 .stream()
697 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
698 p.isEnabled())
699 .findAny().isPresent();
700 }
701
702 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800703 * Checks if data plane interface exists.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800704 *
705 * @param node cordvtn node
706 * @return true if the interface exists, false otherwise
707 */
Hyunsun Moon126171d2016-02-09 01:55:48 -0800708 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800709 return deviceService.getPorts(node.intBrId())
710 .stream()
Hyunsun Moon126171d2016-02-09 01:55:48 -0800711 .filter(p -> getPortName(p).contains(node.dpIntf()) &&
Hyunsun Mooncb799442016-01-15 20:03:18 -0800712 p.isEnabled())
713 .findAny().isPresent();
714 }
715
716 /**
Hyunsun Moon126171d2016-02-09 01:55:48 -0800717 * Checks if the IP addresses are correctly set.
718 *
719 * @param node cordvtn node
720 * @return true if the IP is set, false otherwise
721 */
722 private boolean isIpAddressSet(CordVtnNode node) {
723 Session session = RemoteIpCommandUtil.connect(node.sshInfo());
724 if (session == null) {
725 log.debug("Failed to SSH to {}", node.hostname());
726 return false;
727 }
728
729 Set<IpAddress> intBrIps = RemoteIpCommandUtil.getCurrentIps(session, DEFAULT_BRIDGE);
730 boolean result = RemoteIpCommandUtil.getCurrentIps(session, node.dpIntf()).isEmpty() &&
731 RemoteIpCommandUtil.isInterfaceUp(session, node.dpIntf()) &&
732 intBrIps.contains(node.dpIp().ip()) &&
733 intBrIps.contains(node.localMgmtIp().ip()) &&
734 RemoteIpCommandUtil.isInterfaceUp(session, DEFAULT_BRIDGE);
735
736 RemoteIpCommandUtil.disconnect(session);
737 return result;
738 }
739
740 /**
Hyunsun Mooncb799442016-01-15 20:03:18 -0800741 * Returns connect point of a given port.
742 *
743 * @param port port
744 * @return connect point
745 */
746 private ConnectPoint getConnectPoint(Port port) {
747 return new ConnectPoint(port.element().id(), port.number());
748 }
749
750 /**
751 * Returns connect point of a given host.
752 *
753 * @param host host
754 * @return connect point
755 */
756 private ConnectPoint getConnectPoint(Host host) {
757 return new ConnectPoint(host.location().deviceId(), host.location().port());
758 }
759
760 private class OvsdbHandler implements ConnectionHandler<Device> {
761
762 @Override
763 public void connected(Device device) {
764 CordVtnNode node = getNodeByOvsdbId(device.id());
765 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800766 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800767 } else {
768 log.debug("{} is detected on unregistered node, ignore it.", device.id());
769 }
770 }
771
772 @Override
773 public void disconnected(Device device) {
774 if (!deviceService.isAvailable(device.id())) {
Hyunsun Moon58ddbdc2016-03-07 16:37:17 -0800775 log.debug("Device {} is disconnected", device.id());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800776 adminService.removeDevice(device.id());
777 }
778 }
779 }
780
781 private class BridgeHandler implements ConnectionHandler<Device> {
782
783 @Override
784 public void connected(Device device) {
785 CordVtnNode node = getNodeByBridgeId(device.id());
786 if (node != null) {
Hyunsun Moon126171d2016-02-09 01:55:48 -0800787 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800788 } else {
789 log.debug("{} is detected on unregistered node, ignore it.", device.id());
790 }
791 }
792
793 @Override
794 public void disconnected(Device device) {
795 CordVtnNode node = getNodeByBridgeId(device.id());
796 if (node != null) {
797 log.debug("Integration Bridge is disconnected from {}", node.hostname());
798 setNodeState(node, NodeState.INCOMPLETE);
799 }
800 }
801
802 /**
803 * Handles port added situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800804 * If the added port is tunnel or data plane interface, proceed to the remaining
805 * node initialization. Otherwise, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800806 *
807 * @param port port
808 */
809 public void portAdded(Port port) {
810 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
811 String portName = getPortName(port);
812
813 if (node == null) {
814 log.debug("{} is added to unregistered node, ignore it.", portName);
815 return;
816 }
817
Hyunsun Moonff55e812016-03-10 12:40:16 -0800818 log.info("Port {} is added to {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800819
820 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800821 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800822 cordVtnService.addServiceVm(node, getConnectPoint(port));
823 } else {
824 log.debug("VM is detected on incomplete node, ignore it.", portName);
825 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800826 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
827 setNodeState(node, getNodeState(node));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800828 }
829 }
830
831 /**
832 * Handles port removed situation.
Hyunsun Moon126171d2016-02-09 01:55:48 -0800833 * If the removed port is tunnel or data plane interface, proceed to the remaining
834 * node initialization.Others, do nothing.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800835 *
836 * @param port port
837 */
838 public void portRemoved(Port port) {
839 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
840 String portName = getPortName(port);
841
842 if (node == null) {
843 return;
844 }
845
Hyunsun Moonff55e812016-03-10 12:40:16 -0800846 log.info("Port {} is removed from {}", portName, node.hostname());
Hyunsun Mooncb799442016-01-15 20:03:18 -0800847
848 if (portName.startsWith(VPORT_PREFIX)) {
Hyunsun Moond05b32e2016-03-02 19:27:26 -0800849 if (isNodeStateComplete(node)) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800850 cordVtnService.removeServiceVm(getConnectPoint(port));
851 } else {
852 log.debug("VM is vanished from incomplete node, ignore it.", portName);
853 }
Hyunsun Moon126171d2016-02-09 01:55:48 -0800854 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.dpIntf())) {
Hyunsun Mooncb799442016-01-15 20:03:18 -0800855 setNodeState(node, NodeState.INCOMPLETE);
856 }
857 }
858 }
859
860 private class InternalDeviceListener implements DeviceListener {
861
862 @Override
863 public void event(DeviceEvent event) {
864
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800865 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
866 if (!Objects.equals(localNodeId, leaderNodeId)) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800867 // do not allow to proceed without leadership
Hyunsun Moon7004fcf2016-03-08 04:36:02 -0800868 return;
869 }
870
Hyunsun Mooncb799442016-01-15 20:03:18 -0800871 Device device = event.subject();
872 ConnectionHandler<Device> handler =
873 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
874
875 switch (event.type()) {
876 case PORT_ADDED:
Hyunsun Moonff55e812016-03-10 12:40:16 -0800877 eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800878 break;
879 case PORT_UPDATED:
880 if (!event.port().isEnabled()) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800881 eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800882 }
883 break;
884 case DEVICE_ADDED:
885 case DEVICE_AVAILABILITY_CHANGED:
886 if (deviceService.isAvailable(device.id())) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800887 eventExecutor.execute(() -> handler.connected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800888 } else {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800889 eventExecutor.execute(() -> handler.disconnected(device));
Hyunsun Mooncb799442016-01-15 20:03:18 -0800890 }
891 break;
892 default:
893 break;
894 }
895 }
896 }
897
898 /**
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800899 * Reads cordvtn nodes from config file.
Hyunsun Mooncb799442016-01-15 20:03:18 -0800900 */
901 private void readConfiguration() {
902 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800903 if (config == null) {
Hyunsun Moon3fc17f72016-01-24 21:47:06 -0800904 log.debug("No configuration found");
Hyunsun Mooncb799442016-01-15 20:03:18 -0800905 return;
906 }
907
Hyunsun Moonff55e812016-03-10 12:40:16 -0800908 config.cordVtnNodes().forEach(this::addOrUpdateNode);
Hyunsun Mooncb799442016-01-15 20:03:18 -0800909 }
910
911 private class InternalConfigListener implements NetworkConfigListener {
912
913 @Override
914 public void event(NetworkConfigEvent event) {
Hyunsun Moonff55e812016-03-10 12:40:16 -0800915 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
916 if (!Objects.equals(localNodeId, leaderNodeId)) {
917 // do not allow to proceed without leadership
918 return;
919 }
920
Hyunsun Mooncb799442016-01-15 20:03:18 -0800921 if (!event.configClass().equals(CordVtnConfig.class)) {
922 return;
923 }
924
925 switch (event.type()) {
926 case CONFIG_ADDED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800927 case CONFIG_UPDATED:
Hyunsun Mooncb799442016-01-15 20:03:18 -0800928 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
929 break;
930 default:
931 break;
932 }
933 }
934 }
Hyunsun Moonff55e812016-03-10 12:40:16 -0800935
936 private class InternalMapListener implements MapEventListener<String, CordVtnNode> {
937
938 @Override
939 public void event(MapEvent<String, CordVtnNode> event) {
940 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
941 if (!Objects.equals(localNodeId, leaderNodeId)) {
942 // do not allow to proceed without leadership
943 return;
944 }
945
946 CordVtnNode oldNode;
947 CordVtnNode newNode;
948
949 switch (event.type()) {
950 case UPDATE:
951 oldNode = event.oldValue().value();
952 newNode = event.newValue().value();
953
954 if (!newNode.equals(oldNode)) {
955 log.info("{} has been updated", newNode.hostname());
956 log.debug("New node: {}", newNode);
957 }
958 // perform init procedure based on current state on any updates,
959 // insert, or even if the node is the same for robustness since
960 // it's no harm to run the init procedure multiple times
961 eventExecutor.execute(() -> initNode(newNode));
962 break;
963 case INSERT:
964 newNode = event.newValue().value();
965 log.info("Added {}", newNode.hostname());
966 eventExecutor.execute(() -> initNode(newNode));
967 break;
968 case REMOVE:
969 oldNode = event.oldValue().value();
970 log.info("{} is removed", oldNode.hostname());
971 break;
972 default:
973 break;
974 }
975 }
976 }
Hyunsun Mooncb799442016-01-15 20:03:18 -0800977}