blob: a31917aeddfdbf7d6999d7ae1b4b6df3d501e2cf [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
Hyunsun Moon699f46b2015-12-04 11:35:25 -0800285 @Override
286 public void createServiceDependency(ServiceId tenantServiceId, ServiceId providerServiceId) {
287 }
288
289 @Override
290 public void removeServiceDependency(ServiceId tenantServiceId, ServiceId providerServiceId) {
291 }
292
Hyunsun Moon8539b042015-11-07 22:08:43 -0800293 /**
294 * Returns state of a given cordvtn node.
295 *
296 * @param node cordvtn node
297 * @return node state, or null if no such node exists
298 */
299 private NodeState getNodeState(CordVtnNode node) {
300 checkNotNull(node);
301
302 try {
303 return nodeStore.get(node).value();
304 } catch (NullPointerException e) {
305 log.error("Failed to get state of {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700306 return null;
307 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700308 }
309
Hyunsun Moon8539b042015-11-07 22:08:43 -0800310 /**
311 * Sets a new state for a given cordvtn node.
312 *
313 * @param node cordvtn node
314 * @param newState new node state
315 */
316 private void setNodeState(CordVtnNode node, NodeState newState) {
317 checkNotNull(node);
318
319 log.info("Changed {} state: {}", node.hostname(), newState.toString());
320
321 nodeStore.put(node, newState);
322 newState.process(this, node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700323 }
324
Hyunsun Moon8539b042015-11-07 22:08:43 -0800325 /**
326 * Checks current state of a given cordvtn node and returns it.
327 *
328 * @param node cordvtn node
329 * @return node state
330 */
331 private NodeState checkNodeState(CordVtnNode node) {
332 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700333
Hyunsun Moon8539b042015-11-07 22:08:43 -0800334 if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
335 return NodeState.COMPLETE;
336 } else if (checkIntegrationBridge(node)) {
337 return NodeState.BRIDGE_CREATED;
338 } else if (getOvsdbConnectionState(node)) {
339 return NodeState.OVSDB_CONNECTED;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700340 } else {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800341 return NodeState.INIT;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700342 }
343 }
344
Hyunsun Moon8539b042015-11-07 22:08:43 -0800345 /**
346 * Performs tasks after node initialization.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800347 * First disconnect unnecessary OVSDB connection and then installs flow rules
348 * for existing VMs if there are any.
Hyunsun Moon8539b042015-11-07 22:08:43 -0800349 *
350 * @param node cordvtn node
351 */
352 private void postInit(CordVtnNode node) {
353 disconnect(node);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800354
355 Set<OpenstackNetwork> vNets = Sets.newHashSet();
356 hostService.getConnectedHosts(node.intBrId())
357 .stream()
358 .forEach(host -> {
359 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
360 if (vNet != null) {
361 log.info("VM {} is detected", host.id());
362
363 hostNetworkMap.put(host.id(), vNet.id());
364 vNets.add(vNet);
365 }
366 });
367 vNets.stream().forEach(this::installFlowRules);
Hyunsun Moon8539b042015-11-07 22:08:43 -0800368 }
369
370 /**
371 * Returns connection state of OVSDB server for a given node.
372 *
373 * @param node cordvtn node
374 * @return true if it is connected, false otherwise
375 */
376 private boolean getOvsdbConnectionState(CordVtnNode node) {
377 checkNotNull(node);
378
379 OvsdbClientService ovsdbClient = getOvsdbClient(node);
380 return deviceService.isAvailable(node.ovsdbId()) &&
381 ovsdbClient != null && ovsdbClient.isConnected();
382 }
383
384 /**
385 * Connects to OVSDB server for a given node.
386 *
387 * @param node cordvtn node
388 */
389 private void connect(CordVtnNode node) {
390 checkNotNull(node);
391
392 if (!nodeStore.containsKey(node)) {
393 log.warn("Node {} does not exist", node.hostname());
394 return;
395 }
396
397 if (!getOvsdbConnectionState(node)) {
398 // FIXME remove existing OVSDB device to work around OVSDB device re-connect issue
399 if (deviceService.getDevice(node.ovsdbId()) != null) {
400 adminService.removeDevice(node.ovsdbId());
401 }
402 controller.connect(node.ovsdbIp(), node.ovsdbPort());
403 }
404 }
405
406 /**
407 * Disconnects OVSDB server for a given node.
408 *
409 * @param node cordvtn node
410 */
411 private void disconnect(CordVtnNode node) {
412 checkNotNull(node);
413
414 if (!nodeStore.containsKey(node)) {
415 log.warn("Node {} does not exist", node.hostname());
416 return;
417 }
418
419 if (getOvsdbConnectionState(node)) {
420 OvsdbClientService ovsdbClient = getOvsdbClient(node);
421 ovsdbClient.disconnect();
422 }
423
424 // FIXME remove existing OVSDB device to work around OVSDB device re-connect issue
425 if (deviceService.getDevice(node.ovsdbId()) != null) {
426 adminService.removeDevice(node.ovsdbId());
427 }
428 }
429
430 /**
431 * Returns cordvtn node associated with a given OVSDB device.
432 *
433 * @param ovsdbId OVSDB device id
434 * @return cordvtn node, null if it fails to find the node
435 */
436 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
437 try {
438 return getNodes().stream()
439 .filter(node -> node.ovsdbId().equals(ovsdbId))
440 .findFirst().get();
441 } catch (NoSuchElementException e) {
442 log.debug("Couldn't find node information for {}", ovsdbId);
443 return null;
444 }
445 }
446
447 /**
448 * Returns cordvtn node associated with a given integration bridge.
449 *
450 * @param bridgeId device id of integration bridge
451 * @return cordvtn node, null if it fails to find the node
452 */
453 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
454 try {
455 return getNodes().stream()
456 .filter(node -> node.intBrId().equals(bridgeId))
457 .findFirst().get();
458 } catch (NoSuchElementException e) {
459 log.debug("Couldn't find node information for {}", bridgeId);
460 return null;
461 }
462 }
463
464 /**
465 * Returns OVSDB client for a given node.
466 *
467 * @param node cordvtn node
468 * @return OVSDB client, or null if it fails to get OVSDB client
469 */
470 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
471 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700472
473 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon8539b042015-11-07 22:08:43 -0800474 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700475 if (ovsdbClient == null) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800476 log.debug("Couldn't find OVSDB client for {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700477 }
478 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700479 }
480
Hyunsun Moon8539b042015-11-07 22:08:43 -0800481 /**
482 * Creates an integration bridge for a given node.
483 *
484 * @param node cordvtn node
485 */
486 private void createIntegrationBridge(CordVtnNode node) {
487 if (checkIntegrationBridge(node)) {
488 return;
489 }
490
Hyunsun Moon523d9762015-10-19 12:38:21 -0700491 List<ControllerInfo> controllers = new ArrayList<>();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800492 Sets.newHashSet(clusterService.getNodes()).stream()
Hyunsun Moon523d9762015-10-19 12:38:21 -0700493 .forEach(controller -> {
494 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
495 controllers.add(ctrlInfo);
496 });
Hyunsun Moon8539b042015-11-07 22:08:43 -0800497 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700498
Hyunsun Moon523d9762015-10-19 12:38:21 -0700499 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800500 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700501 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800502 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
Hyunsun Moond772f342015-10-28 20:28:16 -0700503 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800504 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700505 }
506 }
507
Hyunsun Moon8539b042015-11-07 22:08:43 -0800508 /**
509 * Creates tunnel interface to the integration bridge for a given node.
510 *
511 * @param node cordvtn node
512 */
513 private void createTunnelInterface(CordVtnNode node) {
514 if (checkTunnelInterface(node)) {
515 return;
516 }
517
Hyunsun Moond772f342015-10-28 20:28:16 -0700518 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
519 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
520 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
521 }
522 TunnelDescription description =
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800523 new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
Hyunsun Moond772f342015-10-28 20:28:16 -0700524 optionBuilder.build());
525 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800526 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700527 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800528 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
Hyunsun Moond772f342015-10-28 20:28:16 -0700529 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800530 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700531 }
532 }
533
Hyunsun Moon8539b042015-11-07 22:08:43 -0800534 /**
535 * Checks if integration bridge exists and available.
536 *
537 * @param node cordvtn node
538 * @return true if the bridge is available, false otherwise
539 */
540 private boolean checkIntegrationBridge(CordVtnNode node) {
541 return (deviceService.getDevice(node.intBrId()) != null
542 && deviceService.isAvailable(node.intBrId()));
543 }
544
545 /**
546 * Checks if tunnel interface exists.
547 *
548 * @param node cordvtn node
549 * @return true if the interface exists, false otherwise
550 */
551 private boolean checkTunnelInterface(CordVtnNode node) {
Hyunsun Moond772f342015-10-28 20:28:16 -0700552 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800553 deviceService.getPorts(node.intBrId())
554 .stream()
555 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
556 && p.isEnabled())
Hyunsun Moond772f342015-10-28 20:28:16 -0700557 .findAny().get();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800558 return true;
559 } catch (NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700560 return false;
561 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700562 }
563
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800564 /**
565 * Returns tunnel port of the device.
566 *
567 * @param bridgeId device id
568 * @return port, null if no tunnel port exists on a given device
569 */
570 private Port getTunnelPort(DeviceId bridgeId) {
571 try {
572 return deviceService.getPorts(bridgeId).stream()
573 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
574 && p.isEnabled())
575 .findFirst().get();
576 } catch (NoSuchElementException e) {
577 return null;
578 }
579 }
580
581 /**
582 * Returns remote ip address for tunneling.
583 *
584 * @param bridgeId device id
585 * @return ip address, null if no such device exists
586 */
587 private IpAddress getRemoteIp(DeviceId bridgeId) {
588 CordVtnNode node = getNodeByBridgeId(bridgeId);
589 if (node != null) {
590 // TODO get data plane IP for tunneling
591 return node.ovsdbIp();
592 } else {
593 return null;
594 }
595 }
596
597 /**
598 * Returns destination information of all ports associated with a given
599 * OpenStack network. Output of the destination information is set to local
600 * port or tunnel port according to a given device id.
601 *
602 * @param deviceId device id to install flow rules
603 * @param vNet OpenStack network
604 * @return list of flow information, empty list if no flow information exists
605 */
606 private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
607 List<DestinationInfo> dstInfos = Lists.newArrayList();
608 long tunnelId = Long.valueOf(vNet.segmentId());
609
610 for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
611 ConnectPoint cp = getConnectPoint(vPort);
612 if (cp == null) {
613 log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
614 continue;
615 }
616
617 DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
618 DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
619 DestinationInfo.builder(getTunnelPort(deviceId))
620 .setRemoteIp(getRemoteIp(cp.deviceId()));
621
622 dBuilder.setMac(vPort.macAddress())
623 .setTunnelId(tunnelId);
624 dstInfos.add(dBuilder.build());
625 }
626 return dstInfos;
627 }
628
629 /**
630 * Returns local ports associated with a given OpenStack network.
631 *
632 * @param bridgeId device id
633 * @param vNet OpenStack network
634 * @return port list, empty list if no port exists
635 */
636 private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
637 List<Port> ports = new ArrayList<>();
638 openstackService.ports(vNet.id()).stream().forEach(port -> {
639 ConnectPoint cp = getConnectPoint(port);
640 if (cp != null && cp.deviceId().equals(bridgeId)) {
641 ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
642 }
643 });
644 return ports;
645 }
646
647 /**
648 * Returns OpenStack port associated with a given host.
649 *
650 * @param host host
651 * @return OpenStack port, or null if no port has been found
652 */
653 private OpenstackPort getOpenstackPortByHost(Host host) {
654 Port port = deviceService.getPort(host.location().deviceId(),
655 host.location().port());
656 return openstackService.port(port);
657 }
658
659 /**
660 * Returns OpenStack network associated with a given host.
661 *
662 * @param host host
663 * @return OpenStack network, or null if no network has been found
664 */
665 private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
666 OpenstackPort vPort = getOpenstackPortByHost(host);
667 if (vPort != null) {
668 return openstackService.network(vPort.networkId());
669 } else {
670 return null;
671 }
672 }
673
674 /**
675 * Returns port name with OpenStack port information.
676 *
677 * @param vPort OpenStack port
678 * @return port name
679 */
680 private String getPortName(OpenstackPort vPort) {
681 checkNotNull(vPort);
682 return VPORT_PREFIX + vPort.id().substring(0, 10);
683 }
684
685 /**
686 * Returns connect point of a given OpenStack port.
687 * It assumes there's only one physical port associated with an OpenStack port.
688 *
689 * @param vPort openstack port
690 * @return connect point, null if no such port exists
691 */
692 private ConnectPoint getConnectPoint(OpenstackPort vPort) {
693 try {
694 Host host = hostService.getHostsByMac(vPort.macAddress())
695 .stream()
696 .findFirst()
697 .get();
698 return new ConnectPoint(host.location().deviceId(), host.location().port());
699 } catch (NoSuchElementException e) {
700 log.debug("Not a valid host with {}", vPort.macAddress());
701 return null;
702 }
703 }
704
705 /**
706 * Installs flow rules for a given OpenStack network.
707 *
708 * @param vNet OpenStack network
709 */
710 private void installFlowRules(OpenstackNetwork vNet) {
711 checkNotNull(vNet, "Tenant network should not be null");
712
713 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
714 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
715
716 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
717 List<DestinationInfo> localInInfos = dstInfos.stream()
718 .filter(info -> !info.output().equals(inPort))
719 .collect(Collectors.toList());
720 ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
721 }
722
723 Port tunPort = getTunnelPort(device.id());
724 List<DestinationInfo> tunnelInInfos = dstInfos.stream()
725 .filter(info -> !info.output().equals(tunPort))
726 .collect(Collectors.toList());
727 ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
728 }
729 }
730
731 /**
732 * Uninstalls flow rules associated with a given host for a given OpenStack network.
733 *
734 * @param vNet OpenStack network
735 * @param host removed host
736 */
737 private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
738 checkNotNull(vNet, "Tenant network should not be null");
739
740 Port removedPort = deviceService.getPort(host.location().deviceId(),
741 host.location().port());
742
743 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
744 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
745
746 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
747 List<DestinationInfo> localInInfos = Lists.newArrayList(
748 DestinationInfo.builder(getTunnelPort(device.id()))
749 .setTunnelId(Long.valueOf(vNet.segmentId()))
750 .setMac(host.mac())
751 .setRemoteIp(getRemoteIp(host.location().deviceId()))
752 .build());
753 ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
754 }
755
756 if (device.id().equals(host.location().deviceId())) {
757 Port tunPort = getTunnelPort(device.id());
758 List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
759 DestinationInfo.builder(removedPort)
760 .setTunnelId(Long.valueOf(vNet.segmentId()))
761 .setMac(host.mac())
762 .build());
763
764 ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
765 ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
766 }
767 }
768 }
769
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700770 private class InternalDeviceListener implements DeviceListener {
771
772 @Override
773 public void event(DeviceEvent event) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800774
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700775 Device device = event.subject();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800776 ConnectionHandler<Device> handler =
777 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700778
779 switch (event.type()) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800780 case PORT_ADDED:
781 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Moon2b530322015-09-23 13:24:35 -0700782 break;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800783 case PORT_UPDATED:
784 if (!event.port().isEnabled()) {
785 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
786 }
787 break;
788 case DEVICE_ADDED:
Hyunsun Moon2b530322015-09-23 13:24:35 -0700789 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700790 if (deviceService.isAvailable(device.id())) {
791 eventExecutor.submit(() -> handler.connected(device));
792 } else {
793 eventExecutor.submit(() -> handler.disconnected(device));
794 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700795 break;
796 default:
797 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700798 }
799 }
800 }
801
802 private class InternalHostListener implements HostListener {
803
804 @Override
805 public void event(HostEvent event) {
806 Host vm = event.subject();
807
808 switch (event.type()) {
809 case HOST_ADDED:
810 eventExecutor.submit(() -> vmHandler.connected(vm));
811 break;
812 case HOST_REMOVED:
813 eventExecutor.submit(() -> vmHandler.disconnected(vm));
814 break;
815 default:
816 break;
817 }
818 }
819 }
820
Hyunsun Moon2b530322015-09-23 13:24:35 -0700821 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700822
823 @Override
824 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800825 CordVtnNode node = getNodeByOvsdbId(device.id());
826 if (node != null) {
827 setNodeState(node, checkNodeState(node));
Hyunsun Moon523d9762015-10-19 12:38:21 -0700828 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700829 }
830
831 @Override
832 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800833 log.info("OVSDB {} is disconnected", device.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700834 }
835 }
836
837 private class BridgeHandler implements ConnectionHandler<Device> {
838
839 @Override
840 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800841 CordVtnNode node = getNodeByBridgeId(device.id());
842 if (node != null) {
843 setNodeState(node, checkNodeState(node));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700844 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700845 }
846
847 @Override
848 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800849 CordVtnNode node = getNodeByBridgeId(device.id());
850 if (node != null) {
851 log.info("Integration Bridge is disconnected from {}", node.hostname());
852 setNodeState(node, NodeState.INCOMPLETE);
853 }
854 }
855
856 /**
857 * Handles port added situation.
858 * If the added port is tunnel port, proceed remaining node initialization.
859 * Otherwise, do nothing.
860 *
861 * @param port port
862 */
863 public void portAdded(Port port) {
864 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
865 return;
866 }
867
868 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
869 if (node != null) {
870 setNodeState(node, checkNodeState(node));
871 }
872 }
873
874 /**
875 * Handles port removed situation.
876 * If the removed port is tunnel port, proceed remaining node initialization.
877 * Others, do nothing.
878 *
879 * @param port port
880 */
881 public void portRemoved(Port port) {
882 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
883 return;
884 }
885
886 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
887 if (node != null) {
888 log.info("Tunnel interface is removed from {}", node.hostname());
889 setNodeState(node, NodeState.INCOMPLETE);
890 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700891 }
892 }
893
Hyunsun Moon2b530322015-09-23 13:24:35 -0700894 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700895
896 @Override
897 public void connected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800898 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
899 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
900 // do nothing for the host on unregistered or unprepared device
901 return;
902 }
903
904 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
905 if (vNet == null) {
906 return;
907 }
908
Hyunsun Moon1f145552015-10-08 22:25:30 -0700909 log.info("VM {} is detected", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800910
911 hostNetworkMap.put(host.id(), vNet.id());
912 installFlowRules(vNet);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700913 }
914
915 @Override
916 public void disconnected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800917 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
918 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
919 // do nothing for the host on unregistered or unprepared device
920 return;
921 }
922
923 OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
924 if (vNet == null) {
925 return;
926 }
927
Hyunsun Moon1f145552015-10-08 22:25:30 -0700928 log.info("VM {} is vanished", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800929
930 uninstallFlowRules(vNet, host);
931 hostNetworkMap.remove(host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700932 }
933 }
934}