blob: fe196e15edeea3bab587d9e29c20985903803b8a [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
Hyunsun Moonbaf70542015-12-16 14:29:36 -0800267 NodeState state = checkNodeState(node);
Hyunsun Moon8539b042015-11-07 22:08:43 -0800268 state.process(this, node);
269 }
270
271 @Override
272 public boolean getNodeInitState(CordVtnNode node) {
273 checkNotNull(node);
274
275 NodeState state = getNodeState(node);
276 return state != null && state.equals(NodeState.COMPLETE);
277 }
278
Hyunsun Moon699f46b2015-12-04 11:35:25 -0800279 @Override
280 public void createServiceDependency(ServiceId tenantServiceId, ServiceId providerServiceId) {
281 }
282
283 @Override
284 public void removeServiceDependency(ServiceId tenantServiceId, ServiceId providerServiceId) {
285 }
286
Hyunsun Moon8539b042015-11-07 22:08:43 -0800287 /**
288 * Returns state of a given cordvtn node.
289 *
290 * @param node cordvtn node
291 * @return node state, or null if no such node exists
292 */
293 private NodeState getNodeState(CordVtnNode node) {
294 checkNotNull(node);
295
296 try {
297 return nodeStore.get(node).value();
298 } catch (NullPointerException e) {
299 log.error("Failed to get state of {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700300 return null;
301 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700302 }
303
Hyunsun Moon8539b042015-11-07 22:08:43 -0800304 /**
305 * Sets a new state for a given cordvtn node.
306 *
307 * @param node cordvtn node
308 * @param newState new node state
309 */
310 private void setNodeState(CordVtnNode node, NodeState newState) {
311 checkNotNull(node);
312
313 log.info("Changed {} state: {}", node.hostname(), newState.toString());
314
315 nodeStore.put(node, newState);
316 newState.process(this, node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700317 }
318
Hyunsun Moon8539b042015-11-07 22:08:43 -0800319 /**
320 * Checks current state of a given cordvtn node and returns it.
321 *
322 * @param node cordvtn node
323 * @return node state
324 */
325 private NodeState checkNodeState(CordVtnNode node) {
326 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700327
Hyunsun Moon8539b042015-11-07 22:08:43 -0800328 if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
329 return NodeState.COMPLETE;
330 } else if (checkIntegrationBridge(node)) {
331 return NodeState.BRIDGE_CREATED;
332 } else if (getOvsdbConnectionState(node)) {
333 return NodeState.OVSDB_CONNECTED;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700334 } else {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800335 return NodeState.INIT;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700336 }
337 }
338
Hyunsun Moon8539b042015-11-07 22:08:43 -0800339 /**
340 * Performs tasks after node initialization.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800341 * First disconnect unnecessary OVSDB connection and then installs flow rules
342 * for existing VMs if there are any.
Hyunsun Moon8539b042015-11-07 22:08:43 -0800343 *
344 * @param node cordvtn node
345 */
346 private void postInit(CordVtnNode node) {
347 disconnect(node);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800348
349 Set<OpenstackNetwork> vNets = Sets.newHashSet();
350 hostService.getConnectedHosts(node.intBrId())
351 .stream()
352 .forEach(host -> {
353 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
354 if (vNet != null) {
355 log.info("VM {} is detected", host.id());
356
357 hostNetworkMap.put(host.id(), vNet.id());
358 vNets.add(vNet);
359 }
360 });
361 vNets.stream().forEach(this::installFlowRules);
Hyunsun Moon8539b042015-11-07 22:08:43 -0800362 }
363
364 /**
365 * Returns connection state of OVSDB server for a given node.
366 *
367 * @param node cordvtn node
368 * @return true if it is connected, false otherwise
369 */
370 private boolean getOvsdbConnectionState(CordVtnNode node) {
371 checkNotNull(node);
372
373 OvsdbClientService ovsdbClient = getOvsdbClient(node);
374 return deviceService.isAvailable(node.ovsdbId()) &&
375 ovsdbClient != null && ovsdbClient.isConnected();
376 }
377
378 /**
379 * Connects to OVSDB server for a given node.
380 *
381 * @param node cordvtn node
382 */
383 private void connect(CordVtnNode node) {
384 checkNotNull(node);
385
386 if (!nodeStore.containsKey(node)) {
387 log.warn("Node {} does not exist", node.hostname());
388 return;
389 }
390
391 if (!getOvsdbConnectionState(node)) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800392 controller.connect(node.ovsdbIp(), node.ovsdbPort());
393 }
394 }
395
396 /**
397 * Disconnects OVSDB server for a given node.
398 *
399 * @param node cordvtn node
400 */
401 private void disconnect(CordVtnNode node) {
402 checkNotNull(node);
403
404 if (!nodeStore.containsKey(node)) {
405 log.warn("Node {} does not exist", node.hostname());
406 return;
407 }
408
409 if (getOvsdbConnectionState(node)) {
410 OvsdbClientService ovsdbClient = getOvsdbClient(node);
411 ovsdbClient.disconnect();
412 }
Hyunsun Moon8539b042015-11-07 22:08:43 -0800413 }
414
415 /**
416 * Returns cordvtn node associated with a given OVSDB device.
417 *
418 * @param ovsdbId OVSDB device id
419 * @return cordvtn node, null if it fails to find the node
420 */
421 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
422 try {
423 return getNodes().stream()
424 .filter(node -> node.ovsdbId().equals(ovsdbId))
425 .findFirst().get();
426 } catch (NoSuchElementException e) {
427 log.debug("Couldn't find node information for {}", ovsdbId);
428 return null;
429 }
430 }
431
432 /**
433 * Returns cordvtn node associated with a given integration bridge.
434 *
435 * @param bridgeId device id of integration bridge
436 * @return cordvtn node, null if it fails to find the node
437 */
438 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
439 try {
440 return getNodes().stream()
441 .filter(node -> node.intBrId().equals(bridgeId))
442 .findFirst().get();
443 } catch (NoSuchElementException e) {
444 log.debug("Couldn't find node information for {}", bridgeId);
445 return null;
446 }
447 }
448
449 /**
450 * Returns OVSDB client for a given node.
451 *
452 * @param node cordvtn node
453 * @return OVSDB client, or null if it fails to get OVSDB client
454 */
455 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
456 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700457
458 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon8539b042015-11-07 22:08:43 -0800459 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700460 if (ovsdbClient == null) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800461 log.debug("Couldn't find OVSDB client for {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700462 }
463 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700464 }
465
Hyunsun Moon8539b042015-11-07 22:08:43 -0800466 /**
467 * Creates an integration bridge for a given node.
468 *
469 * @param node cordvtn node
470 */
471 private void createIntegrationBridge(CordVtnNode node) {
472 if (checkIntegrationBridge(node)) {
473 return;
474 }
475
Hyunsun Moon523d9762015-10-19 12:38:21 -0700476 List<ControllerInfo> controllers = new ArrayList<>();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800477 Sets.newHashSet(clusterService.getNodes()).stream()
Hyunsun Moon523d9762015-10-19 12:38:21 -0700478 .forEach(controller -> {
479 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
480 controllers.add(ctrlInfo);
481 });
Hyunsun Moon8539b042015-11-07 22:08:43 -0800482 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700483
Hyunsun Moon523d9762015-10-19 12:38:21 -0700484 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800485 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700486 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800487 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
Hyunsun Moond772f342015-10-28 20:28:16 -0700488 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800489 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700490 }
491 }
492
Hyunsun Moon8539b042015-11-07 22:08:43 -0800493 /**
494 * Creates tunnel interface to the integration bridge for a given node.
495 *
496 * @param node cordvtn node
497 */
498 private void createTunnelInterface(CordVtnNode node) {
499 if (checkTunnelInterface(node)) {
500 return;
501 }
502
Hyunsun Moond772f342015-10-28 20:28:16 -0700503 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
504 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
505 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
506 }
507 TunnelDescription description =
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800508 new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
Hyunsun Moond772f342015-10-28 20:28:16 -0700509 optionBuilder.build());
510 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800511 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700512 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800513 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
Hyunsun Moond772f342015-10-28 20:28:16 -0700514 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800515 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700516 }
517 }
518
Hyunsun Moon8539b042015-11-07 22:08:43 -0800519 /**
520 * Checks if integration bridge exists and available.
521 *
522 * @param node cordvtn node
523 * @return true if the bridge is available, false otherwise
524 */
525 private boolean checkIntegrationBridge(CordVtnNode node) {
526 return (deviceService.getDevice(node.intBrId()) != null
527 && deviceService.isAvailable(node.intBrId()));
528 }
529
530 /**
531 * Checks if tunnel interface exists.
532 *
533 * @param node cordvtn node
534 * @return true if the interface exists, false otherwise
535 */
536 private boolean checkTunnelInterface(CordVtnNode node) {
Hyunsun Moond772f342015-10-28 20:28:16 -0700537 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800538 deviceService.getPorts(node.intBrId())
539 .stream()
540 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
541 && p.isEnabled())
Hyunsun Moond772f342015-10-28 20:28:16 -0700542 .findAny().get();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800543 return true;
544 } catch (NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700545 return false;
546 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700547 }
548
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800549 /**
550 * Returns tunnel port of the device.
551 *
552 * @param bridgeId device id
553 * @return port, null if no tunnel port exists on a given device
554 */
555 private Port getTunnelPort(DeviceId bridgeId) {
556 try {
557 return deviceService.getPorts(bridgeId).stream()
558 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
559 && p.isEnabled())
560 .findFirst().get();
561 } catch (NoSuchElementException e) {
562 return null;
563 }
564 }
565
566 /**
567 * Returns remote ip address for tunneling.
568 *
569 * @param bridgeId device id
570 * @return ip address, null if no such device exists
571 */
572 private IpAddress getRemoteIp(DeviceId bridgeId) {
573 CordVtnNode node = getNodeByBridgeId(bridgeId);
574 if (node != null) {
575 // TODO get data plane IP for tunneling
576 return node.ovsdbIp();
577 } else {
578 return null;
579 }
580 }
581
582 /**
583 * Returns destination information of all ports associated with a given
584 * OpenStack network. Output of the destination information is set to local
585 * port or tunnel port according to a given device id.
586 *
587 * @param deviceId device id to install flow rules
588 * @param vNet OpenStack network
589 * @return list of flow information, empty list if no flow information exists
590 */
591 private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
592 List<DestinationInfo> dstInfos = Lists.newArrayList();
593 long tunnelId = Long.valueOf(vNet.segmentId());
594
595 for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
596 ConnectPoint cp = getConnectPoint(vPort);
597 if (cp == null) {
598 log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
599 continue;
600 }
601
602 DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
603 DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
604 DestinationInfo.builder(getTunnelPort(deviceId))
605 .setRemoteIp(getRemoteIp(cp.deviceId()));
606
607 dBuilder.setMac(vPort.macAddress())
608 .setTunnelId(tunnelId);
609 dstInfos.add(dBuilder.build());
610 }
611 return dstInfos;
612 }
613
614 /**
615 * Returns local ports associated with a given OpenStack network.
616 *
617 * @param bridgeId device id
618 * @param vNet OpenStack network
619 * @return port list, empty list if no port exists
620 */
621 private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
622 List<Port> ports = new ArrayList<>();
623 openstackService.ports(vNet.id()).stream().forEach(port -> {
624 ConnectPoint cp = getConnectPoint(port);
625 if (cp != null && cp.deviceId().equals(bridgeId)) {
626 ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
627 }
628 });
629 return ports;
630 }
631
632 /**
633 * Returns OpenStack port associated with a given host.
634 *
635 * @param host host
636 * @return OpenStack port, or null if no port has been found
637 */
638 private OpenstackPort getOpenstackPortByHost(Host host) {
639 Port port = deviceService.getPort(host.location().deviceId(),
640 host.location().port());
641 return openstackService.port(port);
642 }
643
644 /**
645 * Returns OpenStack network associated with a given host.
646 *
647 * @param host host
648 * @return OpenStack network, or null if no network has been found
649 */
650 private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
651 OpenstackPort vPort = getOpenstackPortByHost(host);
652 if (vPort != null) {
653 return openstackService.network(vPort.networkId());
654 } else {
655 return null;
656 }
657 }
658
659 /**
660 * Returns port name with OpenStack port information.
661 *
662 * @param vPort OpenStack port
663 * @return port name
664 */
665 private String getPortName(OpenstackPort vPort) {
666 checkNotNull(vPort);
667 return VPORT_PREFIX + vPort.id().substring(0, 10);
668 }
669
670 /**
671 * Returns connect point of a given OpenStack port.
672 * It assumes there's only one physical port associated with an OpenStack port.
673 *
674 * @param vPort openstack port
675 * @return connect point, null if no such port exists
676 */
677 private ConnectPoint getConnectPoint(OpenstackPort vPort) {
678 try {
679 Host host = hostService.getHostsByMac(vPort.macAddress())
680 .stream()
681 .findFirst()
682 .get();
683 return new ConnectPoint(host.location().deviceId(), host.location().port());
684 } catch (NoSuchElementException e) {
685 log.debug("Not a valid host with {}", vPort.macAddress());
686 return null;
687 }
688 }
689
690 /**
691 * Installs flow rules for a given OpenStack network.
692 *
693 * @param vNet OpenStack network
694 */
695 private void installFlowRules(OpenstackNetwork vNet) {
696 checkNotNull(vNet, "Tenant network should not be null");
697
698 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
699 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
700
701 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
702 List<DestinationInfo> localInInfos = dstInfos.stream()
703 .filter(info -> !info.output().equals(inPort))
704 .collect(Collectors.toList());
705 ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
706 }
707
708 Port tunPort = getTunnelPort(device.id());
709 List<DestinationInfo> tunnelInInfos = dstInfos.stream()
710 .filter(info -> !info.output().equals(tunPort))
711 .collect(Collectors.toList());
712 ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
713 }
714 }
715
716 /**
717 * Uninstalls flow rules associated with a given host for a given OpenStack network.
718 *
719 * @param vNet OpenStack network
720 * @param host removed host
721 */
722 private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
723 checkNotNull(vNet, "Tenant network should not be null");
724
725 Port removedPort = deviceService.getPort(host.location().deviceId(),
726 host.location().port());
727
728 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
729 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
730
731 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
732 List<DestinationInfo> localInInfos = Lists.newArrayList(
733 DestinationInfo.builder(getTunnelPort(device.id()))
734 .setTunnelId(Long.valueOf(vNet.segmentId()))
735 .setMac(host.mac())
736 .setRemoteIp(getRemoteIp(host.location().deviceId()))
737 .build());
738 ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
739 }
740
741 if (device.id().equals(host.location().deviceId())) {
742 Port tunPort = getTunnelPort(device.id());
743 List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
744 DestinationInfo.builder(removedPort)
745 .setTunnelId(Long.valueOf(vNet.segmentId()))
746 .setMac(host.mac())
747 .build());
748
749 ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
750 ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
751 }
752 }
753 }
754
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700755 private class InternalDeviceListener implements DeviceListener {
756
757 @Override
758 public void event(DeviceEvent event) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800759
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700760 Device device = event.subject();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800761 ConnectionHandler<Device> handler =
762 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700763
764 switch (event.type()) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800765 case PORT_ADDED:
766 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Moon2b530322015-09-23 13:24:35 -0700767 break;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800768 case PORT_UPDATED:
769 if (!event.port().isEnabled()) {
770 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
771 }
772 break;
773 case DEVICE_ADDED:
Hyunsun Moon2b530322015-09-23 13:24:35 -0700774 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700775 if (deviceService.isAvailable(device.id())) {
776 eventExecutor.submit(() -> handler.connected(device));
777 } else {
778 eventExecutor.submit(() -> handler.disconnected(device));
779 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700780 break;
781 default:
782 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700783 }
784 }
785 }
786
787 private class InternalHostListener implements HostListener {
788
789 @Override
790 public void event(HostEvent event) {
791 Host vm = event.subject();
792
793 switch (event.type()) {
794 case HOST_ADDED:
795 eventExecutor.submit(() -> vmHandler.connected(vm));
796 break;
797 case HOST_REMOVED:
798 eventExecutor.submit(() -> vmHandler.disconnected(vm));
799 break;
800 default:
801 break;
802 }
803 }
804 }
805
Hyunsun Moon2b530322015-09-23 13:24:35 -0700806 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700807
808 @Override
809 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800810 CordVtnNode node = getNodeByOvsdbId(device.id());
811 if (node != null) {
812 setNodeState(node, checkNodeState(node));
Hyunsun Moon523d9762015-10-19 12:38:21 -0700813 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700814 }
815
816 @Override
817 public void disconnected(Device device) {
Hyunsun Moonba225722015-12-10 13:14:05 -0800818 if (!deviceService.isAvailable(device.id())) {
819 adminService.removeDevice(device.id());
820 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700821 }
822 }
823
824 private class BridgeHandler implements ConnectionHandler<Device> {
825
826 @Override
827 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800828 CordVtnNode node = getNodeByBridgeId(device.id());
829 if (node != null) {
830 setNodeState(node, checkNodeState(node));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700831 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700832 }
833
834 @Override
835 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800836 CordVtnNode node = getNodeByBridgeId(device.id());
837 if (node != null) {
838 log.info("Integration Bridge is disconnected from {}", node.hostname());
839 setNodeState(node, NodeState.INCOMPLETE);
840 }
841 }
842
843 /**
844 * Handles port added situation.
845 * If the added port is tunnel port, proceed remaining node initialization.
846 * Otherwise, do nothing.
847 *
848 * @param port port
849 */
850 public void portAdded(Port port) {
851 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
852 return;
853 }
854
855 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
856 if (node != null) {
857 setNodeState(node, checkNodeState(node));
858 }
859 }
860
861 /**
862 * Handles port removed situation.
863 * If the removed port is tunnel port, proceed remaining node initialization.
864 * Others, do nothing.
865 *
866 * @param port port
867 */
868 public void portRemoved(Port port) {
869 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
870 return;
871 }
872
873 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
874 if (node != null) {
875 log.info("Tunnel interface is removed from {}", node.hostname());
876 setNodeState(node, NodeState.INCOMPLETE);
877 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700878 }
879 }
880
Hyunsun Moon2b530322015-09-23 13:24:35 -0700881 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700882
883 @Override
884 public void connected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800885 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
886 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
887 // do nothing for the host on unregistered or unprepared device
888 return;
889 }
890
891 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
892 if (vNet == null) {
893 return;
894 }
895
Hyunsun Moon1f145552015-10-08 22:25:30 -0700896 log.info("VM {} is detected", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800897
898 hostNetworkMap.put(host.id(), vNet.id());
899 installFlowRules(vNet);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700900 }
901
902 @Override
903 public void disconnected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800904 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
905 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
906 // do nothing for the host on unregistered or unprepared device
907 return;
908 }
909
910 OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
911 if (vNet == null) {
912 return;
913 }
914
Hyunsun Moon1f145552015-10-08 22:25:30 -0700915 log.info("VM {} is vanished", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800916
917 uninstallFlowRules(vNet, host);
918 hostNetworkMap.remove(host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700919 }
920 }
921}