blob: 67297741f5026892cea1e9db14b3029da069c564 [file] [log] [blame]
Hyunsun Moond0e932a2015-09-15 22:39:16 -07001/*
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
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080018import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
Hyunsun Moon1f145552015-10-08 22:25:30 -070020import com.google.common.collect.Sets;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
Hyunsun Moond772f342015-10-28 20:28:16 -070027import org.onlab.util.ItemNotFoundException;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080028import org.onlab.packet.IpAddress;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070029import org.onlab.util.KryoNamespace;
Hyunsun Moon1f145552015-10-08 22:25:30 -070030import org.onosproject.cluster.ClusterService;
31import org.onosproject.core.ApplicationId;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070032import org.onosproject.core.CoreService;
Hyunsun Moond772f342015-10-28 20:28:16 -070033import org.onosproject.net.DefaultAnnotations;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070034import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.Host;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080037import org.onosproject.net.HostId;
Hyunsun Moon8539b042015-11-07 22:08:43 -080038import org.onosproject.net.Port;
Hyunsun Moond772f342015-10-28 20:28:16 -070039import org.onosproject.net.behaviour.BridgeConfig;
40import org.onosproject.net.behaviour.BridgeName;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080041import org.onosproject.net.ConnectPoint;
Hyunsun Moon1f145552015-10-08 22:25:30 -070042import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moond772f342015-10-28 20:28:16 -070043import org.onosproject.net.behaviour.DefaultTunnelDescription;
44import org.onosproject.net.behaviour.TunnelConfig;
45import org.onosproject.net.behaviour.TunnelDescription;
46import org.onosproject.net.behaviour.TunnelName;
Hyunsun Moon8539b042015-11-07 22:08:43 -080047import org.onosproject.net.device.DeviceAdminService;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070048import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
Hyunsun Moond772f342015-10-28 20:28:16 -070051import org.onosproject.net.driver.DriverHandler;
52import org.onosproject.net.driver.DriverService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080053import org.onosproject.net.flowobjective.FlowObjectiveService;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070054import org.onosproject.net.host.HostEvent;
55import org.onosproject.net.host.HostListener;
56import org.onosproject.net.host.HostService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080057import org.onosproject.openstackswitching.OpenstackNetwork;
58import org.onosproject.openstackswitching.OpenstackPort;
59import org.onosproject.openstackswitching.OpenstackSwitchingService;
Hyunsun Moon1f145552015-10-08 22:25:30 -070060import org.onosproject.ovsdb.controller.OvsdbClientService;
61import org.onosproject.ovsdb.controller.OvsdbController;
62import org.onosproject.ovsdb.controller.OvsdbNodeId;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070063import org.onosproject.store.serializers.KryoNamespaces;
Hyunsun Moon1f145552015-10-08 22:25:30 -070064import org.onosproject.store.service.ConsistentMap;
65import org.onosproject.store.service.Serializer;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070066import org.onosproject.store.service.StorageService;
67import org.slf4j.Logger;
68
Hyunsun Moon1f145552015-10-08 22:25:30 -070069import java.util.ArrayList;
70import java.util.HashMap;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070071import java.util.List;
Hyunsun Moon1f145552015-10-08 22:25:30 -070072import java.util.Map;
Hyunsun Moon523d9762015-10-19 12:38:21 -070073import java.util.NoSuchElementException;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080074import java.util.Set;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070075import java.util.concurrent.ExecutorService;
76import java.util.concurrent.Executors;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080077import java.util.stream.Collectors;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070078
Hyunsun Moon1f145552015-10-08 22:25:30 -070079import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070080import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon2b530322015-09-23 13:24:35 -070081import static org.onosproject.net.Device.Type.SWITCH;
Hyunsun Moond772f342015-10-28 20:28:16 -070082import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070083import static org.slf4j.LoggerFactory.getLogger;
84
85/**
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080086 * Provisions virtual tenant networks with service chaining capability
87 * in OpenStack environment.
Hyunsun Moond0e932a2015-09-15 22:39:16 -070088 */
89@Component(immediate = true)
90@Service
91public class CordVtn implements CordVtnService {
92
93 protected final Logger log = getLogger(getClass());
94
Hyunsun Moon2b530322015-09-23 13:24:35 -070095 private static final int NUM_THREADS = 1;
96 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
97 .register(KryoNamespaces.API)
Hyunsun Moon8539b042015-11-07 22:08:43 -080098 .register(CordVtnNode.class)
99 .register(NodeState.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800100 private static final String DEFAULT_BRIDGE = "br-int";
101 private static final String VPORT_PREFIX = "tap";
Hyunsun Moon523d9762015-10-19 12:38:21 -0700102 private static final String DEFAULT_TUNNEL = "vxlan";
103 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700104 {
105 put("key", "flow");
Hyunsun Moon1f145552015-10-08 22:25:30 -0700106 put("remote_ip", "flow");
107 }
108 };
109 private static final int DPID_BEGIN = 3;
110 private static final int OFPORT = 6653;
Hyunsun Moon2b530322015-09-23 13:24:35 -0700111
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected StorageService storageService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700119 protected DeviceService deviceService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected HostService hostService;
123
Hyunsun Moon1f145552015-10-08 22:25:30 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moond772f342015-10-28 20:28:16 -0700125 protected DriverService driverService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon8539b042015-11-07 22:08:43 -0800128 protected DeviceAdminService adminService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800131 protected FlowObjectiveService flowObjectiveService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon1f145552015-10-08 22:25:30 -0700134 protected OvsdbController controller;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected ClusterService clusterService;
138
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected OpenstackSwitchingService openstackService;
141
Hyunsun Moon2b530322015-09-23 13:24:35 -0700142 private final ExecutorService eventExecutor = Executors
143 .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700144
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700145 private final DeviceListener deviceListener = new InternalDeviceListener();
146 private final HostListener hostListener = new InternalHostListener();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700147
148 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700149 private final BridgeHandler bridgeHandler = new BridgeHandler();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700150 private final VmHandler vmHandler = new VmHandler();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700151
Hyunsun Moon8539b042015-11-07 22:08:43 -0800152 private ConsistentMap<CordVtnNode, NodeState> nodeStore;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800153 private Map<HostId, String> hostNetworkMap = Maps.newHashMap();
154 private CordVtnRuleInstaller ruleInstaller;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800155
156 private enum NodeState {
157
158 INIT {
159 @Override
160 public void process(CordVtn cordVtn, CordVtnNode node) {
161 cordVtn.connect(node);
162 }
163 },
164 OVSDB_CONNECTED {
165 @Override
166 public void process(CordVtn cordVtn, CordVtnNode node) {
167 if (!cordVtn.getOvsdbConnectionState(node)) {
168 cordVtn.connect(node);
169 } else {
170 cordVtn.createIntegrationBridge(node);
171 }
172 }
173 },
174 BRIDGE_CREATED {
175 @Override
176 public void process(CordVtn cordVtn, CordVtnNode node) {
177 if (!cordVtn.getOvsdbConnectionState(node)) {
178 cordVtn.connect(node);
179 } else {
180 cordVtn.createTunnelInterface(node);
181 }
182 }
183 },
184 COMPLETE {
185 @Override
186 public void process(CordVtn cordVtn, CordVtnNode node) {
187 cordVtn.postInit(node);
188 }
189 },
190 INCOMPLETE {
191 @Override
192 public void process(CordVtn cordVtn, CordVtnNode node) {
193 }
194 };
195
196 public abstract void process(CordVtn cordVtn, CordVtnNode node);
197 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700198
199 @Activate
200 protected void activate() {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700201 ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
Hyunsun Moon8539b042015-11-07 22:08:43 -0800202 nodeStore = storageService.<CordVtnNode, NodeState>consistentMapBuilder()
Hyunsun Moon1f145552015-10-08 22:25:30 -0700203 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700204 .withName("cordvtn-nodestore")
Hyunsun Moon1f145552015-10-08 22:25:30 -0700205 .withApplicationId(appId)
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700206 .build();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700207
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800208 ruleInstaller = new CordVtnRuleInstaller(appId, flowObjectiveService,
209 driverService, DEFAULT_TUNNEL);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700210 deviceService.addListener(deviceListener);
211 hostService.addListener(hostListener);
Hyunsun Moon2b530322015-09-23 13:24:35 -0700212
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700213 log.info("Started");
214 }
215
216 @Deactivate
217 protected void deactivate() {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700218 deviceService.removeListener(deviceListener);
219 hostService.removeListener(hostListener);
Hyunsun Moon2b530322015-09-23 13:24:35 -0700220
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700221 eventExecutor.shutdown();
Hyunsun Moon1f145552015-10-08 22:25:30 -0700222 nodeStore.clear();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700223
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700224 log.info("Stopped");
225 }
226
227 @Override
Hyunsun Moon8539b042015-11-07 22:08:43 -0800228 public void addNode(CordVtnNode node) {
229 checkNotNull(node);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700230
Hyunsun Moon8539b042015-11-07 22:08:43 -0800231 nodeStore.putIfAbsent(node, checkNodeState(node));
232 initNode(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700233 }
234
235 @Override
Hyunsun Moon8539b042015-11-07 22:08:43 -0800236 public void deleteNode(CordVtnNode node) {
237 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700238
Hyunsun Moon8539b042015-11-07 22:08:43 -0800239 if (getOvsdbConnectionState(node)) {
240 disconnect(node);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700241 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700242
Hyunsun Moon8539b042015-11-07 22:08:43 -0800243 nodeStore.remove(node);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700244 }
245
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700246 @Override
247 public int getNodeCount() {
248 return nodeStore.size();
249 }
250
251 @Override
Hyunsun Moon8539b042015-11-07 22:08:43 -0800252 public List<CordVtnNode> getNodes() {
253 List<CordVtnNode> nodes = new ArrayList<>();
254 nodes.addAll(nodeStore.keySet());
255 return nodes;
256 }
257
258 @Override
259 public void initNode(CordVtnNode node) {
260 checkNotNull(node);
261
262 if (!nodeStore.containsKey(node)) {
263 log.warn("Node {} does not exist, add node first", node.hostname());
264 return;
265 }
266
267 NodeState state = getNodeState(node);
268 if (state == null) {
269 return;
270 } else if (state.equals(NodeState.INCOMPLETE)) {
271 state = checkNodeState(node);
272 }
273
274 state.process(this, node);
275 }
276
277 @Override
278 public boolean getNodeInitState(CordVtnNode node) {
279 checkNotNull(node);
280
281 NodeState state = getNodeState(node);
282 return state != null && state.equals(NodeState.COMPLETE);
283 }
284
285 /**
286 * Returns state of a given cordvtn node.
287 *
288 * @param node cordvtn node
289 * @return node state, or null if no such node exists
290 */
291 private NodeState getNodeState(CordVtnNode node) {
292 checkNotNull(node);
293
294 try {
295 return nodeStore.get(node).value();
296 } catch (NullPointerException e) {
297 log.error("Failed to get state of {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700298 return null;
299 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700300 }
301
Hyunsun Moon8539b042015-11-07 22:08:43 -0800302 /**
303 * Sets a new state for a given cordvtn node.
304 *
305 * @param node cordvtn node
306 * @param newState new node state
307 */
308 private void setNodeState(CordVtnNode node, NodeState newState) {
309 checkNotNull(node);
310
311 log.info("Changed {} state: {}", node.hostname(), newState.toString());
312
313 nodeStore.put(node, newState);
314 newState.process(this, node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700315 }
316
Hyunsun Moon8539b042015-11-07 22:08:43 -0800317 /**
318 * Checks current state of a given cordvtn node and returns it.
319 *
320 * @param node cordvtn node
321 * @return node state
322 */
323 private NodeState checkNodeState(CordVtnNode node) {
324 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700325
Hyunsun Moon8539b042015-11-07 22:08:43 -0800326 if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
327 return NodeState.COMPLETE;
328 } else if (checkIntegrationBridge(node)) {
329 return NodeState.BRIDGE_CREATED;
330 } else if (getOvsdbConnectionState(node)) {
331 return NodeState.OVSDB_CONNECTED;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700332 } else {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800333 return NodeState.INIT;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700334 }
335 }
336
Hyunsun Moon8539b042015-11-07 22:08:43 -0800337 /**
338 * Performs tasks after node initialization.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800339 * First disconnect unnecessary OVSDB connection and then installs flow rules
340 * for existing VMs if there are any.
Hyunsun Moon8539b042015-11-07 22:08:43 -0800341 *
342 * @param node cordvtn node
343 */
344 private void postInit(CordVtnNode node) {
345 disconnect(node);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800346
347 Set<OpenstackNetwork> vNets = Sets.newHashSet();
348 hostService.getConnectedHosts(node.intBrId())
349 .stream()
350 .forEach(host -> {
351 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
352 if (vNet != null) {
353 log.info("VM {} is detected", host.id());
354
355 hostNetworkMap.put(host.id(), vNet.id());
356 vNets.add(vNet);
357 }
358 });
359 vNets.stream().forEach(this::installFlowRules);
Hyunsun Moon8539b042015-11-07 22:08:43 -0800360 }
361
362 /**
363 * Returns connection state of OVSDB server for a given node.
364 *
365 * @param node cordvtn node
366 * @return true if it is connected, false otherwise
367 */
368 private boolean getOvsdbConnectionState(CordVtnNode node) {
369 checkNotNull(node);
370
371 OvsdbClientService ovsdbClient = getOvsdbClient(node);
372 return deviceService.isAvailable(node.ovsdbId()) &&
373 ovsdbClient != null && ovsdbClient.isConnected();
374 }
375
376 /**
377 * Connects to OVSDB server for a given node.
378 *
379 * @param node cordvtn node
380 */
381 private void connect(CordVtnNode node) {
382 checkNotNull(node);
383
384 if (!nodeStore.containsKey(node)) {
385 log.warn("Node {} does not exist", node.hostname());
386 return;
387 }
388
389 if (!getOvsdbConnectionState(node)) {
390 // FIXME remove existing OVSDB device to work around OVSDB device re-connect issue
391 if (deviceService.getDevice(node.ovsdbId()) != null) {
392 adminService.removeDevice(node.ovsdbId());
393 }
394 controller.connect(node.ovsdbIp(), node.ovsdbPort());
395 }
396 }
397
398 /**
399 * Disconnects OVSDB server for a given node.
400 *
401 * @param node cordvtn node
402 */
403 private void disconnect(CordVtnNode node) {
404 checkNotNull(node);
405
406 if (!nodeStore.containsKey(node)) {
407 log.warn("Node {} does not exist", node.hostname());
408 return;
409 }
410
411 if (getOvsdbConnectionState(node)) {
412 OvsdbClientService ovsdbClient = getOvsdbClient(node);
413 ovsdbClient.disconnect();
414 }
415
416 // FIXME remove existing OVSDB device to work around OVSDB device re-connect issue
417 if (deviceService.getDevice(node.ovsdbId()) != null) {
418 adminService.removeDevice(node.ovsdbId());
419 }
420 }
421
422 /**
423 * Returns cordvtn node associated with a given OVSDB device.
424 *
425 * @param ovsdbId OVSDB device id
426 * @return cordvtn node, null if it fails to find the node
427 */
428 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
429 try {
430 return getNodes().stream()
431 .filter(node -> node.ovsdbId().equals(ovsdbId))
432 .findFirst().get();
433 } catch (NoSuchElementException e) {
434 log.debug("Couldn't find node information for {}", ovsdbId);
435 return null;
436 }
437 }
438
439 /**
440 * Returns cordvtn node associated with a given integration bridge.
441 *
442 * @param bridgeId device id of integration bridge
443 * @return cordvtn node, null if it fails to find the node
444 */
445 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
446 try {
447 return getNodes().stream()
448 .filter(node -> node.intBrId().equals(bridgeId))
449 .findFirst().get();
450 } catch (NoSuchElementException e) {
451 log.debug("Couldn't find node information for {}", bridgeId);
452 return null;
453 }
454 }
455
456 /**
457 * Returns OVSDB client for a given node.
458 *
459 * @param node cordvtn node
460 * @return OVSDB client, or null if it fails to get OVSDB client
461 */
462 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
463 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700464
465 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon8539b042015-11-07 22:08:43 -0800466 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700467 if (ovsdbClient == null) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800468 log.debug("Couldn't find OVSDB client for {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700469 }
470 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700471 }
472
Hyunsun Moon8539b042015-11-07 22:08:43 -0800473 /**
474 * Creates an integration bridge for a given node.
475 *
476 * @param node cordvtn node
477 */
478 private void createIntegrationBridge(CordVtnNode node) {
479 if (checkIntegrationBridge(node)) {
480 return;
481 }
482
Hyunsun Moon523d9762015-10-19 12:38:21 -0700483 List<ControllerInfo> controllers = new ArrayList<>();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800484 Sets.newHashSet(clusterService.getNodes()).stream()
Hyunsun Moon523d9762015-10-19 12:38:21 -0700485 .forEach(controller -> {
486 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
487 controllers.add(ctrlInfo);
488 });
Hyunsun Moon8539b042015-11-07 22:08:43 -0800489 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700490
Hyunsun Moon523d9762015-10-19 12:38:21 -0700491 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800492 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700493 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800494 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
Hyunsun Moond772f342015-10-28 20:28:16 -0700495 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800496 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700497 }
498 }
499
Hyunsun Moon8539b042015-11-07 22:08:43 -0800500 /**
501 * Creates tunnel interface to the integration bridge for a given node.
502 *
503 * @param node cordvtn node
504 */
505 private void createTunnelInterface(CordVtnNode node) {
506 if (checkTunnelInterface(node)) {
507 return;
508 }
509
Hyunsun Moond772f342015-10-28 20:28:16 -0700510 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
511 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
512 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
513 }
514 TunnelDescription description =
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800515 new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
Hyunsun Moond772f342015-10-28 20:28:16 -0700516 optionBuilder.build());
517 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800518 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700519 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800520 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
Hyunsun Moond772f342015-10-28 20:28:16 -0700521 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800522 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700523 }
524 }
525
Hyunsun Moon8539b042015-11-07 22:08:43 -0800526 /**
527 * Checks if integration bridge exists and available.
528 *
529 * @param node cordvtn node
530 * @return true if the bridge is available, false otherwise
531 */
532 private boolean checkIntegrationBridge(CordVtnNode node) {
533 return (deviceService.getDevice(node.intBrId()) != null
534 && deviceService.isAvailable(node.intBrId()));
535 }
536
537 /**
538 * Checks if tunnel interface exists.
539 *
540 * @param node cordvtn node
541 * @return true if the interface exists, false otherwise
542 */
543 private boolean checkTunnelInterface(CordVtnNode node) {
Hyunsun Moond772f342015-10-28 20:28:16 -0700544 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800545 deviceService.getPorts(node.intBrId())
546 .stream()
547 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
548 && p.isEnabled())
Hyunsun Moond772f342015-10-28 20:28:16 -0700549 .findAny().get();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800550 return true;
551 } catch (NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700552 return false;
553 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700554 }
555
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800556 /**
557 * Returns tunnel port of the device.
558 *
559 * @param bridgeId device id
560 * @return port, null if no tunnel port exists on a given device
561 */
562 private Port getTunnelPort(DeviceId bridgeId) {
563 try {
564 return deviceService.getPorts(bridgeId).stream()
565 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
566 && p.isEnabled())
567 .findFirst().get();
568 } catch (NoSuchElementException e) {
569 return null;
570 }
571 }
572
573 /**
574 * Returns remote ip address for tunneling.
575 *
576 * @param bridgeId device id
577 * @return ip address, null if no such device exists
578 */
579 private IpAddress getRemoteIp(DeviceId bridgeId) {
580 CordVtnNode node = getNodeByBridgeId(bridgeId);
581 if (node != null) {
582 // TODO get data plane IP for tunneling
583 return node.ovsdbIp();
584 } else {
585 return null;
586 }
587 }
588
589 /**
590 * Returns destination information of all ports associated with a given
591 * OpenStack network. Output of the destination information is set to local
592 * port or tunnel port according to a given device id.
593 *
594 * @param deviceId device id to install flow rules
595 * @param vNet OpenStack network
596 * @return list of flow information, empty list if no flow information exists
597 */
598 private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
599 List<DestinationInfo> dstInfos = Lists.newArrayList();
600 long tunnelId = Long.valueOf(vNet.segmentId());
601
602 for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
603 ConnectPoint cp = getConnectPoint(vPort);
604 if (cp == null) {
605 log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
606 continue;
607 }
608
609 DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
610 DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
611 DestinationInfo.builder(getTunnelPort(deviceId))
612 .setRemoteIp(getRemoteIp(cp.deviceId()));
613
614 dBuilder.setMac(vPort.macAddress())
615 .setTunnelId(tunnelId);
616 dstInfos.add(dBuilder.build());
617 }
618 return dstInfos;
619 }
620
621 /**
622 * Returns local ports associated with a given OpenStack network.
623 *
624 * @param bridgeId device id
625 * @param vNet OpenStack network
626 * @return port list, empty list if no port exists
627 */
628 private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
629 List<Port> ports = new ArrayList<>();
630 openstackService.ports(vNet.id()).stream().forEach(port -> {
631 ConnectPoint cp = getConnectPoint(port);
632 if (cp != null && cp.deviceId().equals(bridgeId)) {
633 ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
634 }
635 });
636 return ports;
637 }
638
639 /**
640 * Returns OpenStack port associated with a given host.
641 *
642 * @param host host
643 * @return OpenStack port, or null if no port has been found
644 */
645 private OpenstackPort getOpenstackPortByHost(Host host) {
646 Port port = deviceService.getPort(host.location().deviceId(),
647 host.location().port());
648 return openstackService.port(port);
649 }
650
651 /**
652 * Returns OpenStack network associated with a given host.
653 *
654 * @param host host
655 * @return OpenStack network, or null if no network has been found
656 */
657 private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
658 OpenstackPort vPort = getOpenstackPortByHost(host);
659 if (vPort != null) {
660 return openstackService.network(vPort.networkId());
661 } else {
662 return null;
663 }
664 }
665
666 /**
667 * Returns port name with OpenStack port information.
668 *
669 * @param vPort OpenStack port
670 * @return port name
671 */
672 private String getPortName(OpenstackPort vPort) {
673 checkNotNull(vPort);
674 return VPORT_PREFIX + vPort.id().substring(0, 10);
675 }
676
677 /**
678 * Returns connect point of a given OpenStack port.
679 * It assumes there's only one physical port associated with an OpenStack port.
680 *
681 * @param vPort openstack port
682 * @return connect point, null if no such port exists
683 */
684 private ConnectPoint getConnectPoint(OpenstackPort vPort) {
685 try {
686 Host host = hostService.getHostsByMac(vPort.macAddress())
687 .stream()
688 .findFirst()
689 .get();
690 return new ConnectPoint(host.location().deviceId(), host.location().port());
691 } catch (NoSuchElementException e) {
692 log.debug("Not a valid host with {}", vPort.macAddress());
693 return null;
694 }
695 }
696
697 /**
698 * Installs flow rules for a given OpenStack network.
699 *
700 * @param vNet OpenStack network
701 */
702 private void installFlowRules(OpenstackNetwork vNet) {
703 checkNotNull(vNet, "Tenant network should not be null");
704
705 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
706 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
707
708 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
709 List<DestinationInfo> localInInfos = dstInfos.stream()
710 .filter(info -> !info.output().equals(inPort))
711 .collect(Collectors.toList());
712 ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
713 }
714
715 Port tunPort = getTunnelPort(device.id());
716 List<DestinationInfo> tunnelInInfos = dstInfos.stream()
717 .filter(info -> !info.output().equals(tunPort))
718 .collect(Collectors.toList());
719 ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
720 }
721 }
722
723 /**
724 * Uninstalls flow rules associated with a given host for a given OpenStack network.
725 *
726 * @param vNet OpenStack network
727 * @param host removed host
728 */
729 private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
730 checkNotNull(vNet, "Tenant network should not be null");
731
732 Port removedPort = deviceService.getPort(host.location().deviceId(),
733 host.location().port());
734
735 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
736 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
737
738 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
739 List<DestinationInfo> localInInfos = Lists.newArrayList(
740 DestinationInfo.builder(getTunnelPort(device.id()))
741 .setTunnelId(Long.valueOf(vNet.segmentId()))
742 .setMac(host.mac())
743 .setRemoteIp(getRemoteIp(host.location().deviceId()))
744 .build());
745 ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
746 }
747
748 if (device.id().equals(host.location().deviceId())) {
749 Port tunPort = getTunnelPort(device.id());
750 List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
751 DestinationInfo.builder(removedPort)
752 .setTunnelId(Long.valueOf(vNet.segmentId()))
753 .setMac(host.mac())
754 .build());
755
756 ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
757 ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
758 }
759 }
760 }
761
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700762 private class InternalDeviceListener implements DeviceListener {
763
764 @Override
765 public void event(DeviceEvent event) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800766
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700767 Device device = event.subject();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800768 ConnectionHandler<Device> handler =
769 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700770
771 switch (event.type()) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800772 case PORT_ADDED:
773 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Moon2b530322015-09-23 13:24:35 -0700774 break;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800775 case PORT_UPDATED:
776 if (!event.port().isEnabled()) {
777 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
778 }
779 break;
780 case DEVICE_ADDED:
Hyunsun Moon2b530322015-09-23 13:24:35 -0700781 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700782 if (deviceService.isAvailable(device.id())) {
783 eventExecutor.submit(() -> handler.connected(device));
784 } else {
785 eventExecutor.submit(() -> handler.disconnected(device));
786 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700787 break;
788 default:
789 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700790 }
791 }
792 }
793
794 private class InternalHostListener implements HostListener {
795
796 @Override
797 public void event(HostEvent event) {
798 Host vm = event.subject();
799
800 switch (event.type()) {
801 case HOST_ADDED:
802 eventExecutor.submit(() -> vmHandler.connected(vm));
803 break;
804 case HOST_REMOVED:
805 eventExecutor.submit(() -> vmHandler.disconnected(vm));
806 break;
807 default:
808 break;
809 }
810 }
811 }
812
Hyunsun Moon2b530322015-09-23 13:24:35 -0700813 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700814
815 @Override
816 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800817 CordVtnNode node = getNodeByOvsdbId(device.id());
818 if (node != null) {
819 setNodeState(node, checkNodeState(node));
Hyunsun Moon523d9762015-10-19 12:38:21 -0700820 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700821 }
822
823 @Override
824 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800825 log.info("OVSDB {} is disconnected", device.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700826 }
827 }
828
829 private class BridgeHandler implements ConnectionHandler<Device> {
830
831 @Override
832 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800833 CordVtnNode node = getNodeByBridgeId(device.id());
834 if (node != null) {
835 setNodeState(node, checkNodeState(node));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700836 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700837 }
838
839 @Override
840 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800841 CordVtnNode node = getNodeByBridgeId(device.id());
842 if (node != null) {
843 log.info("Integration Bridge is disconnected from {}", node.hostname());
844 setNodeState(node, NodeState.INCOMPLETE);
845 }
846 }
847
848 /**
849 * Handles port added situation.
850 * If the added port is tunnel port, proceed remaining node initialization.
851 * Otherwise, do nothing.
852 *
853 * @param port port
854 */
855 public void portAdded(Port port) {
856 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
857 return;
858 }
859
860 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
861 if (node != null) {
862 setNodeState(node, checkNodeState(node));
863 }
864 }
865
866 /**
867 * Handles port removed situation.
868 * If the removed port is tunnel port, proceed remaining node initialization.
869 * Others, do nothing.
870 *
871 * @param port port
872 */
873 public void portRemoved(Port port) {
874 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
875 return;
876 }
877
878 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
879 if (node != null) {
880 log.info("Tunnel interface is removed from {}", node.hostname());
881 setNodeState(node, NodeState.INCOMPLETE);
882 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700883 }
884 }
885
Hyunsun Moon2b530322015-09-23 13:24:35 -0700886 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700887
888 @Override
889 public void connected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800890 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
891 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
892 // do nothing for the host on unregistered or unprepared device
893 return;
894 }
895
896 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
897 if (vNet == null) {
898 return;
899 }
900
Hyunsun Moon1f145552015-10-08 22:25:30 -0700901 log.info("VM {} is detected", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800902
903 hostNetworkMap.put(host.id(), vNet.id());
904 installFlowRules(vNet);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700905 }
906
907 @Override
908 public void disconnected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800909 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
910 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
911 // do nothing for the host on unregistered or unprepared device
912 return;
913 }
914
915 OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
916 if (vNet == null) {
917 return;
918 }
919
Hyunsun Moon1f145552015-10-08 22:25:30 -0700920 log.info("VM {} is vanished", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800921
922 uninstallFlowRules(vNet, host);
923 hostNetworkMap.remove(host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700924 }
925 }
926}