blob: d1ff7f0d84b81551ce0d30bef3a78a4b76409cff [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
Hyunsun Moonbfc47d12015-12-07 14:06:28 -0800280 public void createServiceDependency(CordServiceId tenantCordServiceId,
281 CordServiceId providerCordServiceId) {
282 CordService tenantService = getCordService(tenantCordServiceId);
283 CordService providerService = getCordService(providerCordServiceId);
284
285 // TODO populate flow rules to create service dependency
Hyunsun Moon699f46b2015-12-04 11:35:25 -0800286 }
287
288 @Override
Hyunsun Moonbfc47d12015-12-07 14:06:28 -0800289 public void removeServiceDependency(CordServiceId tenantCordServiceId) {
290 CordService tenantService = getCordService(tenantCordServiceId);
291
292 //TODO uninstall flow rules to remove service dependency
Hyunsun Moon699f46b2015-12-04 11:35:25 -0800293 }
294
Hyunsun Moon8539b042015-11-07 22:08:43 -0800295 /**
296 * Returns state of a given cordvtn node.
297 *
298 * @param node cordvtn node
299 * @return node state, or null if no such node exists
300 */
301 private NodeState getNodeState(CordVtnNode node) {
302 checkNotNull(node);
303
304 try {
305 return nodeStore.get(node).value();
306 } catch (NullPointerException e) {
307 log.error("Failed to get state of {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700308 return null;
309 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700310 }
311
Hyunsun Moon8539b042015-11-07 22:08:43 -0800312 /**
313 * Sets a new state for a given cordvtn node.
314 *
315 * @param node cordvtn node
316 * @param newState new node state
317 */
318 private void setNodeState(CordVtnNode node, NodeState newState) {
319 checkNotNull(node);
320
321 log.info("Changed {} state: {}", node.hostname(), newState.toString());
322
323 nodeStore.put(node, newState);
324 newState.process(this, node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700325 }
326
Hyunsun Moon8539b042015-11-07 22:08:43 -0800327 /**
328 * Checks current state of a given cordvtn node and returns it.
329 *
330 * @param node cordvtn node
331 * @return node state
332 */
333 private NodeState checkNodeState(CordVtnNode node) {
334 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700335
Hyunsun Moon8539b042015-11-07 22:08:43 -0800336 if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
337 return NodeState.COMPLETE;
338 } else if (checkIntegrationBridge(node)) {
339 return NodeState.BRIDGE_CREATED;
340 } else if (getOvsdbConnectionState(node)) {
341 return NodeState.OVSDB_CONNECTED;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700342 } else {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800343 return NodeState.INIT;
Hyunsun Moon1f145552015-10-08 22:25:30 -0700344 }
345 }
346
Hyunsun Moon8539b042015-11-07 22:08:43 -0800347 /**
348 * Performs tasks after node initialization.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800349 * First disconnect unnecessary OVSDB connection and then installs flow rules
350 * for existing VMs if there are any.
Hyunsun Moon8539b042015-11-07 22:08:43 -0800351 *
352 * @param node cordvtn node
353 */
354 private void postInit(CordVtnNode node) {
355 disconnect(node);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800356
357 Set<OpenstackNetwork> vNets = Sets.newHashSet();
358 hostService.getConnectedHosts(node.intBrId())
359 .stream()
360 .forEach(host -> {
361 OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
362 if (vNet != null) {
363 log.info("VM {} is detected", host.id());
364
365 hostNetworkMap.put(host.id(), vNet.id());
366 vNets.add(vNet);
367 }
368 });
369 vNets.stream().forEach(this::installFlowRules);
Hyunsun Moon8539b042015-11-07 22:08:43 -0800370 }
371
372 /**
373 * Returns connection state of OVSDB server for a given node.
374 *
375 * @param node cordvtn node
376 * @return true if it is connected, false otherwise
377 */
378 private boolean getOvsdbConnectionState(CordVtnNode node) {
379 checkNotNull(node);
380
381 OvsdbClientService ovsdbClient = getOvsdbClient(node);
382 return deviceService.isAvailable(node.ovsdbId()) &&
383 ovsdbClient != null && ovsdbClient.isConnected();
384 }
385
386 /**
387 * Connects to OVSDB server for a given node.
388 *
389 * @param node cordvtn node
390 */
391 private void connect(CordVtnNode node) {
392 checkNotNull(node);
393
394 if (!nodeStore.containsKey(node)) {
395 log.warn("Node {} does not exist", node.hostname());
396 return;
397 }
398
399 if (!getOvsdbConnectionState(node)) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800400 controller.connect(node.ovsdbIp(), node.ovsdbPort());
401 }
402 }
403
404 /**
405 * Disconnects OVSDB server for a given node.
406 *
407 * @param node cordvtn node
408 */
409 private void disconnect(CordVtnNode node) {
410 checkNotNull(node);
411
412 if (!nodeStore.containsKey(node)) {
413 log.warn("Node {} does not exist", node.hostname());
414 return;
415 }
416
417 if (getOvsdbConnectionState(node)) {
418 OvsdbClientService ovsdbClient = getOvsdbClient(node);
419 ovsdbClient.disconnect();
420 }
Hyunsun Moon8539b042015-11-07 22:08:43 -0800421 }
422
423 /**
424 * Returns cordvtn node associated with a given OVSDB device.
425 *
426 * @param ovsdbId OVSDB device id
427 * @return cordvtn node, null if it fails to find the node
428 */
429 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
430 try {
431 return getNodes().stream()
432 .filter(node -> node.ovsdbId().equals(ovsdbId))
433 .findFirst().get();
434 } catch (NoSuchElementException e) {
435 log.debug("Couldn't find node information for {}", ovsdbId);
436 return null;
437 }
438 }
439
440 /**
441 * Returns cordvtn node associated with a given integration bridge.
442 *
443 * @param bridgeId device id of integration bridge
444 * @return cordvtn node, null if it fails to find the node
445 */
446 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
447 try {
448 return getNodes().stream()
449 .filter(node -> node.intBrId().equals(bridgeId))
450 .findFirst().get();
451 } catch (NoSuchElementException e) {
452 log.debug("Couldn't find node information for {}", bridgeId);
453 return null;
454 }
455 }
456
457 /**
458 * Returns OVSDB client for a given node.
459 *
460 * @param node cordvtn node
461 * @return OVSDB client, or null if it fails to get OVSDB client
462 */
463 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
464 checkNotNull(node);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700465
466 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
Hyunsun Moon8539b042015-11-07 22:08:43 -0800467 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700468 if (ovsdbClient == null) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800469 log.debug("Couldn't find OVSDB client for {}", node.hostname());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700470 }
471 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700472 }
473
Hyunsun Moon8539b042015-11-07 22:08:43 -0800474 /**
475 * Creates an integration bridge for a given node.
476 *
477 * @param node cordvtn node
478 */
479 private void createIntegrationBridge(CordVtnNode node) {
480 if (checkIntegrationBridge(node)) {
481 return;
482 }
483
Hyunsun Moon523d9762015-10-19 12:38:21 -0700484 List<ControllerInfo> controllers = new ArrayList<>();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800485 Sets.newHashSet(clusterService.getNodes()).stream()
Hyunsun Moon523d9762015-10-19 12:38:21 -0700486 .forEach(controller -> {
487 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
488 controllers.add(ctrlInfo);
489 });
Hyunsun Moon8539b042015-11-07 22:08:43 -0800490 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700491
Hyunsun Moon523d9762015-10-19 12:38:21 -0700492 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800493 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700494 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800495 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
Hyunsun Moond772f342015-10-28 20:28:16 -0700496 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800497 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700498 }
499 }
500
Hyunsun Moon8539b042015-11-07 22:08:43 -0800501 /**
502 * Creates tunnel interface to the integration bridge for a given node.
503 *
504 * @param node cordvtn node
505 */
506 private void createTunnelInterface(CordVtnNode node) {
507 if (checkTunnelInterface(node)) {
508 return;
509 }
510
Hyunsun Moond772f342015-10-28 20:28:16 -0700511 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
512 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
513 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
514 }
515 TunnelDescription description =
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800516 new DefaultTunnelDescription(null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
Hyunsun Moond772f342015-10-28 20:28:16 -0700517 optionBuilder.build());
518 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800519 DriverHandler handler = driverService.createHandler(node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700520 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800521 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
Hyunsun Moond772f342015-10-28 20:28:16 -0700522 } catch (ItemNotFoundException e) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800523 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
Hyunsun Moond772f342015-10-28 20:28:16 -0700524 }
525 }
526
Hyunsun Moon8539b042015-11-07 22:08:43 -0800527 /**
528 * Checks if integration bridge exists and available.
529 *
530 * @param node cordvtn node
531 * @return true if the bridge is available, false otherwise
532 */
533 private boolean checkIntegrationBridge(CordVtnNode node) {
534 return (deviceService.getDevice(node.intBrId()) != null
535 && deviceService.isAvailable(node.intBrId()));
536 }
537
538 /**
539 * Checks if tunnel interface exists.
540 *
541 * @param node cordvtn node
542 * @return true if the interface exists, false otherwise
543 */
544 private boolean checkTunnelInterface(CordVtnNode node) {
Hyunsun Moond772f342015-10-28 20:28:16 -0700545 try {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800546 deviceService.getPorts(node.intBrId())
547 .stream()
548 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
549 && p.isEnabled())
Hyunsun Moond772f342015-10-28 20:28:16 -0700550 .findAny().get();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800551 return true;
552 } catch (NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700553 return false;
554 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700555 }
556
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800557 /**
558 * Returns tunnel port of the device.
559 *
560 * @param bridgeId device id
561 * @return port, null if no tunnel port exists on a given device
562 */
563 private Port getTunnelPort(DeviceId bridgeId) {
564 try {
565 return deviceService.getPorts(bridgeId).stream()
566 .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
567 && p.isEnabled())
568 .findFirst().get();
569 } catch (NoSuchElementException e) {
570 return null;
571 }
572 }
573
574 /**
575 * Returns remote ip address for tunneling.
576 *
577 * @param bridgeId device id
578 * @return ip address, null if no such device exists
579 */
580 private IpAddress getRemoteIp(DeviceId bridgeId) {
581 CordVtnNode node = getNodeByBridgeId(bridgeId);
582 if (node != null) {
583 // TODO get data plane IP for tunneling
584 return node.ovsdbIp();
585 } else {
586 return null;
587 }
588 }
589
590 /**
591 * Returns destination information of all ports associated with a given
592 * OpenStack network. Output of the destination information is set to local
593 * port or tunnel port according to a given device id.
594 *
595 * @param deviceId device id to install flow rules
596 * @param vNet OpenStack network
597 * @return list of flow information, empty list if no flow information exists
598 */
599 private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
600 List<DestinationInfo> dstInfos = Lists.newArrayList();
601 long tunnelId = Long.valueOf(vNet.segmentId());
602
603 for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
604 ConnectPoint cp = getConnectPoint(vPort);
605 if (cp == null) {
606 log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
607 continue;
608 }
609
610 DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
611 DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
612 DestinationInfo.builder(getTunnelPort(deviceId))
613 .setRemoteIp(getRemoteIp(cp.deviceId()));
614
615 dBuilder.setMac(vPort.macAddress())
616 .setTunnelId(tunnelId);
617 dstInfos.add(dBuilder.build());
618 }
619 return dstInfos;
620 }
621
622 /**
623 * Returns local ports associated with a given OpenStack network.
624 *
625 * @param bridgeId device id
626 * @param vNet OpenStack network
627 * @return port list, empty list if no port exists
628 */
629 private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
630 List<Port> ports = new ArrayList<>();
631 openstackService.ports(vNet.id()).stream().forEach(port -> {
632 ConnectPoint cp = getConnectPoint(port);
633 if (cp != null && cp.deviceId().equals(bridgeId)) {
634 ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
635 }
636 });
637 return ports;
638 }
639
640 /**
641 * Returns OpenStack port associated with a given host.
642 *
643 * @param host host
644 * @return OpenStack port, or null if no port has been found
645 */
646 private OpenstackPort getOpenstackPortByHost(Host host) {
647 Port port = deviceService.getPort(host.location().deviceId(),
648 host.location().port());
649 return openstackService.port(port);
650 }
651
652 /**
653 * Returns OpenStack network associated with a given host.
654 *
655 * @param host host
656 * @return OpenStack network, or null if no network has been found
657 */
658 private OpenstackNetwork getOpenstackNetworkByHost(Host host) {
659 OpenstackPort vPort = getOpenstackPortByHost(host);
660 if (vPort != null) {
661 return openstackService.network(vPort.networkId());
662 } else {
663 return null;
664 }
665 }
666
667 /**
668 * Returns port name with OpenStack port information.
669 *
670 * @param vPort OpenStack port
671 * @return port name
672 */
673 private String getPortName(OpenstackPort vPort) {
674 checkNotNull(vPort);
675 return VPORT_PREFIX + vPort.id().substring(0, 10);
676 }
677
678 /**
679 * Returns connect point of a given OpenStack port.
680 * It assumes there's only one physical port associated with an OpenStack port.
681 *
682 * @param vPort openstack port
683 * @return connect point, null if no such port exists
684 */
685 private ConnectPoint getConnectPoint(OpenstackPort vPort) {
686 try {
687 Host host = hostService.getHostsByMac(vPort.macAddress())
688 .stream()
689 .findFirst()
690 .get();
691 return new ConnectPoint(host.location().deviceId(), host.location().port());
692 } catch (NoSuchElementException e) {
693 log.debug("Not a valid host with {}", vPort.macAddress());
694 return null;
695 }
696 }
697
698 /**
Hyunsun Moonbfc47d12015-12-07 14:06:28 -0800699 * Returns OpenStack network associated with a given CORD service.
700 *
701 * @param serviceId service id
702 * @return cord service, or null if it fails to get network from OpenStack
703 */
704 private CordService getCordService(CordServiceId serviceId) {
705 OpenstackNetwork vNet = openstackService.network(serviceId.id());
706 if (vNet == null) {
707 log.warn("Couldn't find OpenStack network for service {}", serviceId.id());
708 return null;
709 }
710
711 // TODO create CordService with network/subnet information from Neutron
712 return null;
713 }
714
715 /**
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800716 * Installs flow rules for a given OpenStack network.
717 *
718 * @param vNet OpenStack network
719 */
720 private void installFlowRules(OpenstackNetwork vNet) {
721 checkNotNull(vNet, "Tenant network should not be null");
722
723 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
724 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
725
726 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
727 List<DestinationInfo> localInInfos = dstInfos.stream()
728 .filter(info -> !info.output().equals(inPort))
729 .collect(Collectors.toList());
730 ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
731 }
732
733 Port tunPort = getTunnelPort(device.id());
734 List<DestinationInfo> tunnelInInfos = dstInfos.stream()
735 .filter(info -> !info.output().equals(tunPort))
736 .collect(Collectors.toList());
737 ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
738 }
739 }
740
741 /**
742 * Uninstalls flow rules associated with a given host for a given OpenStack network.
743 *
744 * @param vNet OpenStack network
745 * @param host removed host
746 */
747 private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
748 checkNotNull(vNet, "Tenant network should not be null");
749
750 Port removedPort = deviceService.getPort(host.location().deviceId(),
751 host.location().port());
752
753 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
754 List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
755
756 for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
757 List<DestinationInfo> localInInfos = Lists.newArrayList(
758 DestinationInfo.builder(getTunnelPort(device.id()))
759 .setTunnelId(Long.valueOf(vNet.segmentId()))
760 .setMac(host.mac())
761 .setRemoteIp(getRemoteIp(host.location().deviceId()))
762 .build());
763 ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
764 }
765
766 if (device.id().equals(host.location().deviceId())) {
767 Port tunPort = getTunnelPort(device.id());
768 List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
769 DestinationInfo.builder(removedPort)
770 .setTunnelId(Long.valueOf(vNet.segmentId()))
771 .setMac(host.mac())
772 .build());
773
774 ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
775 ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
776 }
777 }
778 }
779
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700780 private class InternalDeviceListener implements DeviceListener {
781
782 @Override
783 public void event(DeviceEvent event) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800784
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700785 Device device = event.subject();
Hyunsun Moon8539b042015-11-07 22:08:43 -0800786 ConnectionHandler<Device> handler =
787 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700788
789 switch (event.type()) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800790 case PORT_ADDED:
791 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
Hyunsun Moon2b530322015-09-23 13:24:35 -0700792 break;
Hyunsun Moon8539b042015-11-07 22:08:43 -0800793 case PORT_UPDATED:
794 if (!event.port().isEnabled()) {
795 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
796 }
797 break;
798 case DEVICE_ADDED:
Hyunsun Moon2b530322015-09-23 13:24:35 -0700799 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700800 if (deviceService.isAvailable(device.id())) {
801 eventExecutor.submit(() -> handler.connected(device));
802 } else {
803 eventExecutor.submit(() -> handler.disconnected(device));
804 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700805 break;
806 default:
807 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700808 }
809 }
810 }
811
812 private class InternalHostListener implements HostListener {
813
814 @Override
815 public void event(HostEvent event) {
816 Host vm = event.subject();
817
818 switch (event.type()) {
819 case HOST_ADDED:
820 eventExecutor.submit(() -> vmHandler.connected(vm));
821 break;
822 case HOST_REMOVED:
823 eventExecutor.submit(() -> vmHandler.disconnected(vm));
824 break;
825 default:
826 break;
827 }
828 }
829 }
830
Hyunsun Moon2b530322015-09-23 13:24:35 -0700831 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700832
833 @Override
834 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800835 CordVtnNode node = getNodeByOvsdbId(device.id());
836 if (node != null) {
837 setNodeState(node, checkNodeState(node));
Hyunsun Moon523d9762015-10-19 12:38:21 -0700838 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700839 }
840
841 @Override
842 public void disconnected(Device device) {
Hyunsun Moonba225722015-12-10 13:14:05 -0800843 if (!deviceService.isAvailable(device.id())) {
844 adminService.removeDevice(device.id());
845 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700846 }
847 }
848
849 private class BridgeHandler implements ConnectionHandler<Device> {
850
851 @Override
852 public void connected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800853 CordVtnNode node = getNodeByBridgeId(device.id());
854 if (node != null) {
855 setNodeState(node, checkNodeState(node));
Hyunsun Moon1f145552015-10-08 22:25:30 -0700856 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700857 }
858
859 @Override
860 public void disconnected(Device device) {
Hyunsun Moon8539b042015-11-07 22:08:43 -0800861 CordVtnNode node = getNodeByBridgeId(device.id());
862 if (node != null) {
863 log.info("Integration Bridge is disconnected from {}", node.hostname());
864 setNodeState(node, NodeState.INCOMPLETE);
865 }
866 }
867
868 /**
869 * Handles port added situation.
870 * If the added port is tunnel port, proceed remaining node initialization.
871 * Otherwise, do nothing.
872 *
873 * @param port port
874 */
875 public void portAdded(Port port) {
876 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
877 return;
878 }
879
880 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
881 if (node != null) {
882 setNodeState(node, checkNodeState(node));
883 }
884 }
885
886 /**
887 * Handles port removed situation.
888 * If the removed port is tunnel port, proceed remaining node initialization.
889 * Others, do nothing.
890 *
891 * @param port port
892 */
893 public void portRemoved(Port port) {
894 if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
895 return;
896 }
897
898 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
899 if (node != null) {
900 log.info("Tunnel interface is removed from {}", node.hostname());
901 setNodeState(node, NodeState.INCOMPLETE);
902 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700903 }
904 }
905
Hyunsun Moon2b530322015-09-23 13:24:35 -0700906 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700907
908 @Override
909 public void connected(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 = getOpenstackNetworkByHost(host);
917 if (vNet == null) {
918 return;
919 }
920
Hyunsun Moon1f145552015-10-08 22:25:30 -0700921 log.info("VM {} is detected", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800922
923 hostNetworkMap.put(host.id(), vNet.id());
924 installFlowRules(vNet);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700925 }
926
927 @Override
928 public void disconnected(Host host) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800929 CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
930 if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
931 // do nothing for the host on unregistered or unprepared device
932 return;
933 }
934
935 OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
936 if (vNet == null) {
937 return;
938 }
939
Hyunsun Moon1f145552015-10-08 22:25:30 -0700940 log.info("VM {} is vanished", host.id());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800941
942 uninstallFlowRules(vNet, host);
943 hostNetworkMap.remove(host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700944 }
945 }
946}