blob: d56b3b67b29d27500f909d5a0e22c128c4fa73ff [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)) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800398 controller.connect(node.ovsdbIp(), node.ovsdbPort());
399 }
400 }
401
402 /**
403 * Disconnects OVSDB server for a given node.
404 *
405 * @param node cordvtn node
406 */
407 private void disconnect(CordVtnNode node) {
408 checkNotNull(node);
409
410 if (!nodeStore.containsKey(node)) {
411 log.warn("Node {} does not exist", node.hostname());
412 return;
413 }
414
415 if (getOvsdbConnectionState(node)) {
416 OvsdbClientService ovsdbClient = getOvsdbClient(node);
417 ovsdbClient.disconnect();
418 }
Hyunsun Moon8539b042015-11-07 22:08:43 -0800419 }
420
421 /**
422 * Returns cordvtn node associated with a given OVSDB device.
423 *
424 * @param ovsdbId OVSDB device id
425 * @return cordvtn node, null if it fails to find the node
426 */
427 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
428 try {
429 return getNodes().stream()
430 .filter(node -> node.ovsdbId().equals(ovsdbId))
431 .findFirst().get();
432 } catch (NoSuchElementException e) {
433 log.debug("Couldn't find node information for {}", ovsdbId);
434 return null;
435 }
436 }
437
438 /**
439 * Returns cordvtn node associated with a given integration bridge.
440 *
441 * @param bridgeId device id of integration bridge
442 * @return cordvtn node, null if it fails to find the node
443 */
444 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
445 try {
446 return getNodes().stream()
447 .filter(node -> node.intBrId().equals(bridgeId))
448 .findFirst().get();
449 } catch (NoSuchElementException e) {
450 log.debug("Couldn't find node information for {}", bridgeId);
451 return null;
452 }
453 }
454
455 /**
456 * Returns OVSDB client for a given node.
457 *
458 * @param node cordvtn node
459 * @return OVSDB client, or null if it fails to get OVSDB client
460 */
461 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
462 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700463
464 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon8539b042015-11-07 22:08:43 -0800465 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700466 if (ovsdbClient == null) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800467 log.debug("Couldn't find OVSDB client for {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700468 }
469 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700470 }
471
Hyunsun Moon8539b042015-11-07 22:08:43 -0800472 /**
473 * Creates an integration bridge for a given node.
474 *
475 * @param node cordvtn node
476 */
477 private void createIntegrationBridge(CordVtnNode node) {
478 if (checkIntegrationBridge(node)) {
479 return;
480 }
481
Hyunsun Moon523d9762015-10-19 12:38:21 -0700482 List<ControllerInfo> controllers = new ArrayList<>();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800483 Sets.newHashSet(clusterService.getNodes()).stream()
Hyunsun Moon523d9762015-10-19 12:38:21 -0700484 .forEach(controller -> {
485 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
486 controllers.add(ctrlInfo);
487 });
Hyunsun Moon8539b042015-11-07 22:08:43 -0800488 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700489
Hyunsun Moon523d9762015-10-19 12:38:21 -0700490 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800491 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700492 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800493 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
Hyunsun Moond772f342015-10-28 20:28:16 -0700494 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800495 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700496 }
497 }
498
Hyunsun Moon8539b042015-11-07 22:08:43 -0800499 /**
500 * Creates tunnel interface to the integration bridge for a given node.
501 *
502 * @param node cordvtn node
503 */
504 private void createTunnelInterface(CordVtnNode node) {
505 if (checkTunnelInterface(node)) {
506 return;
507 }
508
Hyunsun Moond772f342015-10-28 20:28:16 -0700509 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
510 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
511 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
512 }
513 TunnelDescription description =
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800514 new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
Hyunsun Moond772f342015-10-28 20:28:16 -0700515 optionBuilder.build());
516 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800517 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700518 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800519 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
Hyunsun Moond772f342015-10-28 20:28:16 -0700520 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800521 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700522 }
523 }
524
Hyunsun Moon8539b042015-11-07 22:08:43 -0800525 /**
526 * Checks if integration bridge exists and available.
527 *
528 * @param node cordvtn node
529 * @return true if the bridge is available, false otherwise
530 */
531 private boolean checkIntegrationBridge(CordVtnNode node) {
532 return (deviceService.getDevice(node.intBrId()) != null
533 && deviceService.isAvailable(node.intBrId()));
534 }
535
536 /**
537 * Checks if tunnel interface exists.
538 *
539 * @param node cordvtn node
540 * @return true if the interface exists, false otherwise
541 */
542 private boolean checkTunnelInterface(CordVtnNode node) {
Hyunsun Moond772f342015-10-28 20:28:16 -0700543 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800544 deviceService.getPorts(node.intBrId())
545 .stream()
546 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
547 && p.isEnabled())
Hyunsun Moond772f342015-10-28 20:28:16 -0700548 .findAny().get();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800549 return true;
550 } catch (NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700551 return false;
552 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700553 }
554
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800555 /**
556 * Returns tunnel port of the device.
557 *
558 * @param bridgeId device id
559 * @return port, null if no tunnel port exists on a given device
560 */
561 private Port getTunnelPort(DeviceId bridgeId) {
562 try {
563 return deviceService.getPorts(bridgeId).stream()
564 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
565 && p.isEnabled())
566 .findFirst().get();
567 } catch (NoSuchElementException e) {
568 return null;
569 }
570 }
571
572 /**
573 * Returns remote ip address for tunneling.
574 *
575 * @param bridgeId device id
576 * @return ip address, null if no such device exists
577 */
578 private IpAddress getRemoteIp(DeviceId bridgeId) {
579 CordVtnNode node = getNodeByBridgeId(bridgeId);
580 if (node != null) {
581 // TODO get data plane IP for tunneling
582 return node.ovsdbIp();
583 } else {
584 return null;
585 }
586 }
587
588 /**
589 * Returns destination information of all ports associated with a given
590 * OpenStack network. Output of the destination information is set to local
591 * port or tunnel port according to a given device id.
592 *
593 * @param deviceId device id to install flow rules
594 * @param vNet OpenStack network
595 * @return list of flow information, empty list if no flow information exists
596 */
597 private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
598 List<DestinationInfo> dstInfos = Lists.newArrayList();
599 long tunnelId = Long.valueOf(vNet.segmentId());
600
601 for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
602 ConnectPoint cp = getConnectPoint(vPort);
603 if (cp == null) {
604 log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
605 continue;
606 }
607
608 DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
609 DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
610 DestinationInfo.builder(getTunnelPort(deviceId))
611 .setRemoteIp(getRemoteIp(cp.deviceId()));
612
613 dBuilder.setMac(vPort.macAddress())
614 .setTunnelId(tunnelId);
615 dstInfos.add(dBuilder.build());
616 }
617 return dstInfos;
618 }
619
620 /**
621 * Returns local ports associated with a given OpenStack network.
622 *
623 * @param bridgeId device id
624 * @param vNet OpenStack network
625 * @return port list, empty list if no port exists
626 */
627 private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
628 List<Port> ports = new ArrayList<>();
629 openstackService.ports(vNet.id()).stream().forEach(port -> {
630 ConnectPoint cp = getConnectPoint(port);
631 if (cp != null && cp.deviceId().equals(bridgeId)) {
632 ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
633 }
634 });
635 return ports;
636 }
637
638 /**
639 * Returns OpenStack port associated with a given host.
640 *
641 * @param host host
642 * @return OpenStack port, or null if no port has been found
643 */
644 private OpenstackPort getOpenstackPortByHost(Host host) {
645 Port port = deviceService.getPort(host.location().deviceId(),
646 host.location().port());
647 return openstackService.port(port);
648 }
649
650 /**
651 * Returns OpenStack network associated with a given host.
652 *
653 * @param host host
654 * @return OpenStack network, or null if no network has been found
655 */
656 private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
657 OpenstackPort vPort = getOpenstackPortByHost(host);
658 if (vPort != null) {
659 return openstackService.network(vPort.networkId());
660 } else {
661 return null;
662 }
663 }
664
665 /**
666 * Returns port name with OpenStack port information.
667 *
668 * @param vPort OpenStack port
669 * @return port name
670 */
671 private String getPortName(OpenstackPort vPort) {
672 checkNotNull(vPort);
673 return VPORT_PREFIX + vPort.id().substring(0, 10);
674 }
675
676 /**
677 * Returns connect point of a given OpenStack port.
678 * It assumes there's only one physical port associated with an OpenStack port.
679 *
680 * @param vPort openstack port
681 * @return connect point, null if no such port exists
682 */
683 private ConnectPoint getConnectPoint(OpenstackPort vPort) {
684 try {
685 Host host = hostService.getHostsByMac(vPort.macAddress())
686 .stream()
687 .findFirst()
688 .get();
689 return new ConnectPoint(host.location().deviceId(), host.location().port());
690 } catch (NoSuchElementException e) {
691 log.debug("Not a valid host with {}", vPort.macAddress());
692 return null;
693 }
694 }
695
696 /**
697 * Installs flow rules for a given OpenStack network.
698 *
699 * @param vNet OpenStack network
700 */
701 private void installFlowRules(OpenstackNetwork vNet) {
702 checkNotNull(vNet, "Tenant network should not be null");
703
704 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
705 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
706
707 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
708 List<DestinationInfo> localInInfos = dstInfos.stream()
709 .filter(info -> !info.output().equals(inPort))
710 .collect(Collectors.toList());
711 ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
712 }
713
714 Port tunPort = getTunnelPort(device.id());
715 List<DestinationInfo> tunnelInInfos = dstInfos.stream()
716 .filter(info -> !info.output().equals(tunPort))
717 .collect(Collectors.toList());
718 ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
719 }
720 }
721
722 /**
723 * Uninstalls flow rules associated with a given host for a given OpenStack network.
724 *
725 * @param vNet OpenStack network
726 * @param host removed host
727 */
728 private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
729 checkNotNull(vNet, "Tenant network should not be null");
730
731 Port removedPort = deviceService.getPort(host.location().deviceId(),
732 host.location().port());
733
734 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
735 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
736
737 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
738 List<DestinationInfo> localInInfos = Lists.newArrayList(
739 DestinationInfo.builder(getTunnelPort(device.id()))
740 .setTunnelId(Long.valueOf(vNet.segmentId()))
741 .setMac(host.mac())
742 .setRemoteIp(getRemoteIp(host.location().deviceId()))
743 .build());
744 ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
745 }
746
747 if (device.id().equals(host.location().deviceId())) {
748 Port tunPort = getTunnelPort(device.id());
749 List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
750 DestinationInfo.builder(removedPort)
751 .setTunnelId(Long.valueOf(vNet.segmentId()))
752 .setMac(host.mac())
753 .build());
754
755 ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
756 ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
757 }
758 }
759 }
760
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700761 private class InternalDeviceListener implements DeviceListener {
762
763 @Override
764 public void event(DeviceEvent event) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800765
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700766 Device device = event.subject();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800767 ConnectionHandler<Device> handler =
768 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700769
770 switch (event.type()) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800771 case PORT_ADDED:
772 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Moon2b530322015-09-23 13:24:35 -0700773 break;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800774 case PORT_UPDATED:
775 if (!event.port().isEnabled()) {
776 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
777 }
778 break;
779 case DEVICE_ADDED:
Hyunsun Moon2b530322015-09-23 13:24:35 -0700780 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700781 if (deviceService.isAvailable(device.id())) {
782 eventExecutor.submit(() -> handler.connected(device));
783 } else {
784 eventExecutor.submit(() -> handler.disconnected(device));
785 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700786 break;
787 default:
788 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700789 }
790 }
791 }
792
793 private class InternalHostListener implements HostListener {
794
795 @Override
796 public void event(HostEvent event) {
797 Host vm = event.subject();
798
799 switch (event.type()) {
800 case HOST_ADDED:
801 eventExecutor.submit(() -> vmHandler.connected(vm));
802 break;
803 case HOST_REMOVED:
804 eventExecutor.submit(() -> vmHandler.disconnected(vm));
805 break;
806 default:
807 break;
808 }
809 }
810 }
811
Hyunsun Moon2b530322015-09-23 13:24:35 -0700812 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700813
814 @Override
815 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800816 CordVtnNode node = getNodeByOvsdbId(device.id());
817 if (node != null) {
818 setNodeState(node, checkNodeState(node));
Hyunsun Moon523d9762015-10-19 12:38:21 -0700819 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700820 }
821
822 @Override
823 public void disconnected(Device device) {
Hyunsun Moonba225722015-12-10 13:14:05 -0800824 if (!deviceService.isAvailable(device.id())) {
825 adminService.removeDevice(device.id());
826 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700827 }
828 }
829
830 private class BridgeHandler implements ConnectionHandler<Device> {
831
832 @Override
833 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800834 CordVtnNode node = getNodeByBridgeId(device.id());
835 if (node != null) {
836 setNodeState(node, checkNodeState(node));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700837 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700838 }
839
840 @Override
841 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800842 CordVtnNode node = getNodeByBridgeId(device.id());
843 if (node != null) {
844 log.info("Integration Bridge is disconnected from {}", node.hostname());
845 setNodeState(node, NodeState.INCOMPLETE);
846 }
847 }
848
849 /**
850 * Handles port added situation.
851 * If the added port is tunnel port, proceed remaining node initialization.
852 * Otherwise, do nothing.
853 *
854 * @param port port
855 */
856 public void portAdded(Port port) {
857 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
858 return;
859 }
860
861 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
862 if (node != null) {
863 setNodeState(node, checkNodeState(node));
864 }
865 }
866
867 /**
868 * Handles port removed situation.
869 * If the removed port is tunnel port, proceed remaining node initialization.
870 * Others, do nothing.
871 *
872 * @param port port
873 */
874 public void portRemoved(Port port) {
875 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
876 return;
877 }
878
879 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
880 if (node != null) {
881 log.info("Tunnel interface is removed from {}", node.hostname());
882 setNodeState(node, NodeState.INCOMPLETE);
883 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700884 }
885 }
886
Hyunsun Moon2b530322015-09-23 13:24:35 -0700887 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700888
889 @Override
890 public void connected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800891 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
892 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
893 // do nothing for the host on unregistered or unprepared device
894 return;
895 }
896
897 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
898 if (vNet == null) {
899 return;
900 }
901
Hyunsun Moon1f145552015-10-08 22:25:30 -0700902 log.info("VM {} is detected", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800903
904 hostNetworkMap.put(host.id(), vNet.id());
905 installFlowRules(vNet);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700906 }
907
908 @Override
909 public void disconnected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800910 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
911 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
912 // do nothing for the host on unregistered or unprepared device
913 return;
914 }
915
916 OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
917 if (vNet == null) {
918 return;
919 }
920
Hyunsun Moon1f145552015-10-08 22:25:30 -0700921 log.info("VM {} is vanished", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800922
923 uninstallFlowRules(vNet, host);
924 hostNetworkMap.remove(host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700925 }
926 }
927}