blob: e15bc7632a5c6e351c0c737b992e7b5694805848 [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 Moon1f145552015-10-08 22:25:30 -070018import com.google.common.collect.Collections2;
19import com.google.common.collect.Sets;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Hyunsun Moond772f342015-10-28 20:28:16 -070026import org.onlab.util.ItemNotFoundException;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070027import org.onlab.util.KryoNamespace;
Hyunsun Moon1f145552015-10-08 22:25:30 -070028import org.onosproject.cluster.ClusterService;
29import org.onosproject.core.ApplicationId;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070030import org.onosproject.core.CoreService;
Hyunsun Moond772f342015-10-28 20:28:16 -070031import org.onosproject.net.DefaultAnnotations;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070032import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Host;
Hyunsun Moond772f342015-10-28 20:28:16 -070035import org.onosproject.net.behaviour.BridgeConfig;
36import org.onosproject.net.behaviour.BridgeName;
Hyunsun Moon1f145552015-10-08 22:25:30 -070037import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moond772f342015-10-28 20:28:16 -070038import org.onosproject.net.behaviour.DefaultTunnelDescription;
39import org.onosproject.net.behaviour.TunnelConfig;
40import org.onosproject.net.behaviour.TunnelDescription;
41import org.onosproject.net.behaviour.TunnelName;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070042import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
44import org.onosproject.net.device.DeviceService;
Hyunsun Moond772f342015-10-28 20:28:16 -070045import org.onosproject.net.driver.DriverHandler;
46import org.onosproject.net.driver.DriverService;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070047import org.onosproject.net.host.HostEvent;
48import org.onosproject.net.host.HostListener;
49import org.onosproject.net.host.HostService;
Hyunsun Moon1f145552015-10-08 22:25:30 -070050import org.onosproject.ovsdb.controller.OvsdbClientService;
51import org.onosproject.ovsdb.controller.OvsdbController;
52import org.onosproject.ovsdb.controller.OvsdbNodeId;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070053import org.onosproject.store.serializers.KryoNamespaces;
Hyunsun Moon1f145552015-10-08 22:25:30 -070054import org.onosproject.store.service.ConsistentMap;
55import org.onosproject.store.service.Serializer;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070056import org.onosproject.store.service.StorageService;
Hyunsun Moon1f145552015-10-08 22:25:30 -070057import org.onosproject.store.service.Versioned;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070058import org.slf4j.Logger;
59
Hyunsun Moon1f145552015-10-08 22:25:30 -070060import java.util.ArrayList;
61import java.util.HashMap;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070062import java.util.List;
Hyunsun Moon1f145552015-10-08 22:25:30 -070063import java.util.Map;
Hyunsun Moon523d9762015-10-19 12:38:21 -070064import java.util.NoSuchElementException;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070065import java.util.concurrent.ExecutorService;
66import java.util.concurrent.Executors;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070067
Hyunsun Moon1f145552015-10-08 22:25:30 -070068import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070069import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon2b530322015-09-23 13:24:35 -070070import static org.onosproject.net.Device.Type.SWITCH;
Hyunsun Moond772f342015-10-28 20:28:16 -070071import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
Hyunsun Moond0e932a2015-09-15 22:39:16 -070072import static org.slf4j.LoggerFactory.getLogger;
73
74/**
Hyunsun Moon2b530322015-09-23 13:24:35 -070075 * Provides initial setup or cleanup for provisioning virtual tenant networks
76 * on ovsdb, integration bridge and vm when they are added or deleted.
Hyunsun Moond0e932a2015-09-15 22:39:16 -070077 */
78@Component(immediate = true)
79@Service
80public class CordVtn implements CordVtnService {
81
82 protected final Logger log = getLogger(getClass());
83
Hyunsun Moon2b530322015-09-23 13:24:35 -070084 private static final int NUM_THREADS = 1;
85 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
86 .register(KryoNamespaces.API)
Hyunsun Moon1f145552015-10-08 22:25:30 -070087 .register(DefaultOvsdbNode.class);
88 private static final String DEFAULT_BRIDGE_NAME = "br-int";
Hyunsun Moon523d9762015-10-19 12:38:21 -070089 private static final String DEFAULT_TUNNEL = "vxlan";
90 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
Hyunsun Moon1f145552015-10-08 22:25:30 -070091 {
92 put("key", "flow");
Hyunsun Moon1f145552015-10-08 22:25:30 -070093 put("remote_ip", "flow");
94 }
95 };
96 private static final int DPID_BEGIN = 3;
97 private static final int OFPORT = 6653;
Hyunsun Moon2b530322015-09-23 13:24:35 -070098
Hyunsun Moond0e932a2015-09-15 22:39:16 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected StorageService storageService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700106 protected DeviceService deviceService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected HostService hostService;
110
Hyunsun Moon1f145552015-10-08 22:25:30 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moond772f342015-10-28 20:28:16 -0700112 protected DriverService driverService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon1f145552015-10-08 22:25:30 -0700115 protected OvsdbController controller;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ClusterService clusterService;
119
Hyunsun Moon2b530322015-09-23 13:24:35 -0700120 private final ExecutorService eventExecutor = Executors
121 .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700122
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700123 private final DeviceListener deviceListener = new InternalDeviceListener();
124 private final HostListener hostListener = new InternalHostListener();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700125
126 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700127 private final BridgeHandler bridgeHandler = new BridgeHandler();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700128 private final VmHandler vmHandler = new VmHandler();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700129
Hyunsun Moon1f145552015-10-08 22:25:30 -0700130 private ConsistentMap<DeviceId, OvsdbNode> nodeStore;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700131
132 @Activate
133 protected void activate() {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700134 ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
Hyunsun Moon1f145552015-10-08 22:25:30 -0700135 nodeStore = storageService.<DeviceId, OvsdbNode>consistentMapBuilder()
136 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700137 .withName("cordvtn-nodestore")
Hyunsun Moon1f145552015-10-08 22:25:30 -0700138 .withApplicationId(appId)
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700139 .build();
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700140
141 deviceService.addListener(deviceListener);
142 hostService.addListener(hostListener);
Hyunsun Moon2b530322015-09-23 13:24:35 -0700143
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700144 log.info("Started");
145 }
146
147 @Deactivate
148 protected void deactivate() {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700149 deviceService.removeListener(deviceListener);
150 hostService.removeListener(hostListener);
Hyunsun Moon2b530322015-09-23 13:24:35 -0700151
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700152 eventExecutor.shutdown();
Hyunsun Moon1f145552015-10-08 22:25:30 -0700153 nodeStore.clear();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700154
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700155 log.info("Stopped");
156 }
157
158 @Override
Hyunsun Moon1f145552015-10-08 22:25:30 -0700159 public void addNode(OvsdbNode ovsdb) {
160 checkNotNull(ovsdb);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700161
Hyunsun Moonc10f7772015-10-20 13:58:02 -0700162 nodeStore.putIfAbsent(ovsdb.deviceId(), ovsdb);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700163
164 if (isNodeConnected(ovsdb)) {
165 init(ovsdb);
166 } else {
167 connect(ovsdb);
168 }
Hyunsun Moon1f145552015-10-08 22:25:30 -0700169 }
170
171 @Override
172 public void deleteNode(OvsdbNode ovsdb) {
173 checkNotNull(ovsdb);
174
Hyunsun Moon523d9762015-10-19 12:38:21 -0700175 if (deviceService.getDevice(ovsdb.deviceId()) != null) {
176 if (deviceService.isAvailable(ovsdb.deviceId())) {
177 log.warn("Cannot delete connected node {}", ovsdb.host());
178 return;
179 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700180 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700181 nodeStore.remove(ovsdb.deviceId());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700182 }
183
184 @Override
Hyunsun Moon1f145552015-10-08 22:25:30 -0700185 public void connect(OvsdbNode ovsdb) {
186 checkNotNull(ovsdb);
187
188 if (!nodeStore.containsKey(ovsdb.deviceId())) {
189 log.warn("Node {} does not exist", ovsdb.host());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700190 return;
191 }
Hyunsun Moon523d9762015-10-19 12:38:21 -0700192
193 if (!isNodeConnected(ovsdb)) {
194 controller.connect(ovsdb.ip(), ovsdb.port());
195 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700196 }
197
198 @Override
Hyunsun Moon1f145552015-10-08 22:25:30 -0700199 public void disconnect(OvsdbNode ovsdb) {
200 checkNotNull(ovsdb);
201
202 if (!nodeStore.containsKey(ovsdb.deviceId())) {
203 log.warn("Node {} does not exist", ovsdb.host());
Hyunsun Moon2b530322015-09-23 13:24:35 -0700204 return;
205 }
Hyunsun Moon1f145552015-10-08 22:25:30 -0700206
Hyunsun Moon523d9762015-10-19 12:38:21 -0700207 if (isNodeConnected(ovsdb)) {
208 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700209 ovsdbClient.disconnect();
210 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700211 }
212
Hyunsun Moon523d9762015-10-19 12:38:21 -0700213 private void init(OvsdbNode ovsdb) {
214 checkNotNull(ovsdb);
215
216 if (!nodeStore.containsKey(ovsdb.deviceId())) {
217 log.warn("Node {} does not exist", ovsdb.host());
218 return;
219 }
220
221 if (!isNodeConnected(ovsdb)) {
222 log.warn("Node {} is not connected", ovsdb.host());
223 return;
224 }
225
226 if (deviceService.getDevice(ovsdb.intBrId()) == null ||
227 !deviceService.isAvailable(ovsdb.intBrId())) {
228 createIntegrationBridge(ovsdb);
Hyunsun Moond772f342015-10-28 20:28:16 -0700229 } else if (!checkVxlanInterface(ovsdb)) {
230 createVxlanInterface(ovsdb);
Hyunsun Moon523d9762015-10-19 12:38:21 -0700231 }
232 }
233
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700234 @Override
235 public int getNodeCount() {
236 return nodeStore.size();
237 }
238
239 @Override
Hyunsun Moon2b530322015-09-23 13:24:35 -0700240 public OvsdbNode getNode(DeviceId deviceId) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700241 Versioned<OvsdbNode> ovsdb = nodeStore.get(deviceId);
242 if (ovsdb != null) {
243 return ovsdb.value();
244 } else {
245 return null;
246 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700247 }
248
249 @Override
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700250 public List<OvsdbNode> getNodes() {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700251 List<OvsdbNode> ovsdbs = new ArrayList<>();
252 ovsdbs.addAll(Collections2.transform(nodeStore.values(), Versioned::value));
253 return ovsdbs;
254 }
255
256 @Override
257 public boolean isNodeConnected(OvsdbNode ovsdb) {
258 checkNotNull(ovsdb);
259
260 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
261 if (ovsdbClient == null) {
262 return false;
263 } else {
264 return ovsdbClient.isConnected();
265 }
266 }
267
268 private OvsdbClientService getOvsdbClient(OvsdbNode ovsdb) {
269 checkNotNull(ovsdb);
270
271 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
272 new OvsdbNodeId(ovsdb.ip(), ovsdb.port().toInt()));
273 if (ovsdbClient == null) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700274 log.debug("Couldn't find ovsdb client for {}", ovsdb.host());
Hyunsun Moon1f145552015-10-08 22:25:30 -0700275 }
276 return ovsdbClient;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700277 }
278
Hyunsun Moon523d9762015-10-19 12:38:21 -0700279 private void createIntegrationBridge(OvsdbNode ovsdb) {
280 List<ControllerInfo> controllers = new ArrayList<>();
281 Sets.newHashSet(clusterService.getNodes())
282 .forEach(controller -> {
283 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
284 controllers.add(ctrlInfo);
285 });
286 String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN);
287
Hyunsun Moon523d9762015-10-19 12:38:21 -0700288 try {
Hyunsun Moond772f342015-10-28 20:28:16 -0700289 DriverHandler handler = driverService.createHandler(ovsdb.deviceId());
290 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
291 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME), dpid, controllers);
292 } catch (ItemNotFoundException e) {
293 log.warn("Failed to create integration bridge on {}", ovsdb.deviceId());
294 }
295 }
296
297 private void createVxlanInterface(OvsdbNode ovsdb) {
298 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
299 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
300 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
301 }
302 TunnelDescription description =
303 new DefaultTunnelDescription(null, null, VXLAN,
304 TunnelName.tunnelName(DEFAULT_TUNNEL),
305 optionBuilder.build());
306 try {
307 DriverHandler handler = driverService.createHandler(ovsdb.deviceId());
308 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
309 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME), description);
310 } catch (ItemNotFoundException e) {
311 log.warn("Failed to create VXLAN interface on {}", ovsdb.deviceId());
312 }
313 }
314
315 private boolean checkVxlanInterface(OvsdbNode ovsdb) {
316 try {
317 DriverHandler handler = driverService.createHandler(ovsdb.deviceId());
318 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
319 bridgeConfig.getPorts().stream()
320 .filter(p -> p.annotations().value("portName").equals(DEFAULT_TUNNEL))
321 .findAny().get();
322 } catch (ItemNotFoundException | NoSuchElementException e) {
Hyunsun Moon523d9762015-10-19 12:38:21 -0700323 return false;
324 }
325 return true;
326 }
327
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700328 private class InternalDeviceListener implements DeviceListener {
329
330 @Override
331 public void event(DeviceEvent event) {
332 Device device = event.subject();
Hyunsun Moon2b530322015-09-23 13:24:35 -0700333 ConnectionHandler handler = (device.type() == SWITCH ? bridgeHandler : ovsdbHandler);
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700334
335 switch (event.type()) {
Hyunsun Moon2b530322015-09-23 13:24:35 -0700336 case DEVICE_ADDED:
337 eventExecutor.submit(() -> handler.connected(device));
338 break;
339 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moon523d9762015-10-19 12:38:21 -0700340 if (deviceService.isAvailable(device.id())) {
341 eventExecutor.submit(() -> handler.connected(device));
342 } else {
343 eventExecutor.submit(() -> handler.disconnected(device));
344 }
Hyunsun Moon2b530322015-09-23 13:24:35 -0700345 break;
346 default:
347 break;
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700348 }
349 }
350 }
351
352 private class InternalHostListener implements HostListener {
353
354 @Override
355 public void event(HostEvent event) {
356 Host vm = event.subject();
357
358 switch (event.type()) {
359 case HOST_ADDED:
360 eventExecutor.submit(() -> vmHandler.connected(vm));
361 break;
362 case HOST_REMOVED:
363 eventExecutor.submit(() -> vmHandler.disconnected(vm));
364 break;
365 default:
366 break;
367 }
368 }
369 }
370
Hyunsun Moon2b530322015-09-23 13:24:35 -0700371 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700372
373 @Override
374 public void connected(Device device) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700375 log.info("Ovsdb {} is connected", device.id());
376
Hyunsun Moon1f145552015-10-08 22:25:30 -0700377 OvsdbNode ovsdb = getNode(device.id());
Hyunsun Moon523d9762015-10-19 12:38:21 -0700378 if (ovsdb != null) {
379 init(ovsdb);
380 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700381 }
382
383 @Override
384 public void disconnected(Device device) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700385 log.warn("Ovsdb {} is disconnected", device.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700386 }
387 }
388
389 private class BridgeHandler implements ConnectionHandler<Device> {
390
391 @Override
392 public void connected(Device device) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700393 log.info("Integration Bridge {} is detected", device.id());
394
Hyunsun Moon523d9762015-10-19 12:38:21 -0700395 OvsdbNode ovsdb;
396 try {
397 ovsdb = getNodes().stream()
398 .filter(node -> node.intBrId().equals(device.id()))
399 .findFirst().get();
400 } catch (NoSuchElementException e) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700401 log.warn("Couldn't find OVSDB associated with {}", device.id());
402 return;
403 }
404
Hyunsun Moond772f342015-10-28 20:28:16 -0700405 if (!checkVxlanInterface(ovsdb)) {
406 createVxlanInterface(ovsdb);
Hyunsun Moon1f145552015-10-08 22:25:30 -0700407 }
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700408 }
409
410 @Override
411 public void disconnected(Device device) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700412 log.info("Integration Bridge {} is vanished", device.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700413 }
414 }
415
Hyunsun Moon2b530322015-09-23 13:24:35 -0700416 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700417
418 @Override
419 public void connected(Host host) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700420 log.info("VM {} is detected", host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700421 }
422
423 @Override
424 public void disconnected(Host host) {
Hyunsun Moon1f145552015-10-08 22:25:30 -0700425 log.info("VM {} is vanished", host.id());
Hyunsun Moond0e932a2015-09-15 22:39:16 -0700426 }
427 }
428}