blob: 2813205b56dca877525731133fca5b65e65a504c [file] [log] [blame]
Hyunsun Moone9d75992015-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 Moon7dca9b32015-10-08 22:25:30 -070018import com.google.common.collect.Collections2;
19import com.google.common.collect.Sets;
Hyunsun Moone9d75992015-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 Moone9d75992015-09-15 22:39:16 -070026import org.onlab.util.KryoNamespace;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070027import org.onosproject.cluster.ClusterService;
28import org.onosproject.core.ApplicationId;
Hyunsun Moone9d75992015-09-15 22:39:16 -070029import org.onosproject.core.CoreService;
Hyunsun Moone9d75992015-09-15 22:39:16 -070030import org.onosproject.net.Device;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.Host;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070033import org.onosproject.net.behaviour.ControllerInfo;
Hyunsun Moone9d75992015-09-15 22:39:16 -070034import org.onosproject.net.device.DeviceEvent;
35import org.onosproject.net.device.DeviceListener;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.host.HostEvent;
38import org.onosproject.net.host.HostListener;
39import org.onosproject.net.host.HostService;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070040import org.onosproject.ovsdb.controller.OvsdbClientService;
41import org.onosproject.ovsdb.controller.OvsdbController;
42import org.onosproject.ovsdb.controller.OvsdbNodeId;
Hyunsun Moone9d75992015-09-15 22:39:16 -070043import org.onosproject.store.serializers.KryoNamespaces;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070044import org.onosproject.store.service.ConsistentMap;
45import org.onosproject.store.service.Serializer;
Hyunsun Moone9d75992015-09-15 22:39:16 -070046import org.onosproject.store.service.StorageService;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070047import org.onosproject.store.service.Versioned;
Hyunsun Moone9d75992015-09-15 22:39:16 -070048import org.slf4j.Logger;
49
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070050import java.util.ArrayList;
51import java.util.HashMap;
Hyunsun Moone9d75992015-09-15 22:39:16 -070052import java.util.List;
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070053import java.util.Map;
Hyunsun Moonb525d5c2015-10-19 12:38:21 -070054import java.util.NoSuchElementException;
Hyunsun Moone9d75992015-09-15 22:39:16 -070055import java.util.concurrent.ExecutorService;
56import java.util.concurrent.Executors;
Hyunsun Moone9d75992015-09-15 22:39:16 -070057
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070058import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moone9d75992015-09-15 22:39:16 -070059import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon9661d642015-09-23 13:24:35 -070060import static org.onosproject.net.Device.Type.SWITCH;
Hyunsun Moone9d75992015-09-15 22:39:16 -070061import static org.slf4j.LoggerFactory.getLogger;
62
63/**
Hyunsun Moon9661d642015-09-23 13:24:35 -070064 * Provides initial setup or cleanup for provisioning virtual tenant networks
65 * on ovsdb, integration bridge and vm when they are added or deleted.
Hyunsun Moone9d75992015-09-15 22:39:16 -070066 */
67@Component(immediate = true)
68@Service
69public class CordVtn implements CordVtnService {
70
71 protected final Logger log = getLogger(getClass());
72
Hyunsun Moon9661d642015-09-23 13:24:35 -070073 private static final int NUM_THREADS = 1;
74 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
75 .register(KryoNamespaces.API)
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070076 .register(DefaultOvsdbNode.class);
77 private static final String DEFAULT_BRIDGE_NAME = "br-int";
Hyunsun Moonb525d5c2015-10-19 12:38:21 -070078 private static final String DEFAULT_TUNNEL = "vxlan";
79 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -070080 {
81 put("key", "flow");
82 put("local_ip", "flow");
83 put("remote_ip", "flow");
84 }
85 };
86 private static final int DPID_BEGIN = 3;
87 private static final int OFPORT = 6653;
Hyunsun Moon9661d642015-09-23 13:24:35 -070088
Hyunsun Moone9d75992015-09-15 22:39:16 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected CoreService coreService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected StorageService storageService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moone9d75992015-09-15 22:39:16 -070096 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected HostService hostService;
100
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700102 protected OvsdbController controller;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ClusterService clusterService;
106
Hyunsun Moon9661d642015-09-23 13:24:35 -0700107 private final ExecutorService eventExecutor = Executors
108 .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
Hyunsun Moone9d75992015-09-15 22:39:16 -0700109
Hyunsun Moone9d75992015-09-15 22:39:16 -0700110 private final DeviceListener deviceListener = new InternalDeviceListener();
111 private final HostListener hostListener = new InternalHostListener();
Hyunsun Moon9661d642015-09-23 13:24:35 -0700112
113 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
Hyunsun Moone9d75992015-09-15 22:39:16 -0700114 private final BridgeHandler bridgeHandler = new BridgeHandler();
Hyunsun Moon9661d642015-09-23 13:24:35 -0700115 private final VmHandler vmHandler = new VmHandler();
Hyunsun Moone9d75992015-09-15 22:39:16 -0700116
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700117 private ConsistentMap<DeviceId, OvsdbNode> nodeStore;
Hyunsun Moone9d75992015-09-15 22:39:16 -0700118
119 @Activate
120 protected void activate() {
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700121 ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700122 nodeStore = storageService.<DeviceId, OvsdbNode>consistentMapBuilder()
123 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
Hyunsun Moone9d75992015-09-15 22:39:16 -0700124 .withName("cordvtn-nodestore")
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700125 .withApplicationId(appId)
Hyunsun Moone9d75992015-09-15 22:39:16 -0700126 .build();
Hyunsun Moone9d75992015-09-15 22:39:16 -0700127
128 deviceService.addListener(deviceListener);
129 hostService.addListener(hostListener);
Hyunsun Moon9661d642015-09-23 13:24:35 -0700130
Hyunsun Moone9d75992015-09-15 22:39:16 -0700131 log.info("Started");
132 }
133
134 @Deactivate
135 protected void deactivate() {
Hyunsun Moone9d75992015-09-15 22:39:16 -0700136 deviceService.removeListener(deviceListener);
137 hostService.removeListener(hostListener);
Hyunsun Moon9661d642015-09-23 13:24:35 -0700138
Hyunsun Moone9d75992015-09-15 22:39:16 -0700139 eventExecutor.shutdown();
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700140 nodeStore.clear();
Hyunsun Moon9661d642015-09-23 13:24:35 -0700141
Hyunsun Moone9d75992015-09-15 22:39:16 -0700142 log.info("Stopped");
143 }
144
145 @Override
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700146 public void addNode(OvsdbNode ovsdb) {
147 checkNotNull(ovsdb);
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700148
149 if (!nodeStore.containsKey(ovsdb.deviceId())) {
150 nodeStore.put(ovsdb.deviceId(), ovsdb);
151 }
152
153 if (isNodeConnected(ovsdb)) {
154 init(ovsdb);
155 } else {
156 connect(ovsdb);
157 }
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700158 }
159
160 @Override
161 public void deleteNode(OvsdbNode ovsdb) {
162 checkNotNull(ovsdb);
163
164 if (!nodeStore.containsKey(ovsdb.deviceId())) {
Hyunsun Moone9d75992015-09-15 22:39:16 -0700165 return;
166 }
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700167
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700168 if (deviceService.getDevice(ovsdb.deviceId()) != null) {
169 if (deviceService.isAvailable(ovsdb.deviceId())) {
170 log.warn("Cannot delete connected node {}", ovsdb.host());
171 return;
172 }
Hyunsun Moon9661d642015-09-23 13:24:35 -0700173 }
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700174 nodeStore.remove(ovsdb.deviceId());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700175 }
176
177 @Override
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700178 public void connect(OvsdbNode ovsdb) {
179 checkNotNull(ovsdb);
180
181 if (!nodeStore.containsKey(ovsdb.deviceId())) {
182 log.warn("Node {} does not exist", ovsdb.host());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700183 return;
184 }
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700185
186 if (!isNodeConnected(ovsdb)) {
187 controller.connect(ovsdb.ip(), ovsdb.port());
188 }
Hyunsun Moon9661d642015-09-23 13:24:35 -0700189 }
190
191 @Override
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700192 public void disconnect(OvsdbNode ovsdb) {
193 checkNotNull(ovsdb);
194
195 if (!nodeStore.containsKey(ovsdb.deviceId())) {
196 log.warn("Node {} does not exist", ovsdb.host());
Hyunsun Moon9661d642015-09-23 13:24:35 -0700197 return;
198 }
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700199
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700200 if (isNodeConnected(ovsdb)) {
201 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700202 ovsdbClient.disconnect();
203 }
Hyunsun Moone9d75992015-09-15 22:39:16 -0700204 }
205
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700206 private void init(OvsdbNode ovsdb) {
207 checkNotNull(ovsdb);
208
209 if (!nodeStore.containsKey(ovsdb.deviceId())) {
210 log.warn("Node {} does not exist", ovsdb.host());
211 return;
212 }
213
214 if (!isNodeConnected(ovsdb)) {
215 log.warn("Node {} is not connected", ovsdb.host());
216 return;
217 }
218
219 if (deviceService.getDevice(ovsdb.intBrId()) == null ||
220 !deviceService.isAvailable(ovsdb.intBrId())) {
221 createIntegrationBridge(ovsdb);
222 } else if (!checkVxlanPort(ovsdb)) {
223 createVxlanPort(ovsdb);
224 }
225 }
226
Hyunsun Moone9d75992015-09-15 22:39:16 -0700227 @Override
228 public int getNodeCount() {
229 return nodeStore.size();
230 }
231
232 @Override
Hyunsun Moon9661d642015-09-23 13:24:35 -0700233 public OvsdbNode getNode(DeviceId deviceId) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700234 Versioned<OvsdbNode> ovsdb = nodeStore.get(deviceId);
235 if (ovsdb != null) {
236 return ovsdb.value();
237 } else {
238 return null;
239 }
Hyunsun Moon9661d642015-09-23 13:24:35 -0700240 }
241
242 @Override
Hyunsun Moone9d75992015-09-15 22:39:16 -0700243 public List<OvsdbNode> getNodes() {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700244 List<OvsdbNode> ovsdbs = new ArrayList<>();
245 ovsdbs.addAll(Collections2.transform(nodeStore.values(), Versioned::value));
246 return ovsdbs;
247 }
248
249 @Override
250 public boolean isNodeConnected(OvsdbNode ovsdb) {
251 checkNotNull(ovsdb);
252
253 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
254 if (ovsdbClient == null) {
255 return false;
256 } else {
257 return ovsdbClient.isConnected();
258 }
259 }
260
261 private OvsdbClientService getOvsdbClient(OvsdbNode ovsdb) {
262 checkNotNull(ovsdb);
263
264 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
265 new OvsdbNodeId(ovsdb.ip(), ovsdb.port().toInt()));
266 if (ovsdbClient == null) {
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700267 log.debug("Couldn't find ovsdb client for {}", ovsdb.host());
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700268 }
269 return ovsdbClient;
Hyunsun Moone9d75992015-09-15 22:39:16 -0700270 }
271
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700272 private void createIntegrationBridge(OvsdbNode ovsdb) {
273 List<ControllerInfo> controllers = new ArrayList<>();
274 Sets.newHashSet(clusterService.getNodes())
275 .forEach(controller -> {
276 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
277 controllers.add(ctrlInfo);
278 });
279 String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN);
280
281 // TODO change to use bridge config
282 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
283 ovsdbClient.createBridge(DEFAULT_BRIDGE_NAME, dpid, controllers);
284 }
285
286 private void createVxlanPort(OvsdbNode ovsdb) {
287 // TODO change to use tunnel config and tunnel description
288 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
289 ovsdbClient.createTunnel(DEFAULT_BRIDGE_NAME, DEFAULT_TUNNEL,
290 DEFAULT_TUNNEL, DEFAULT_TUNNEL_OPTIONS);
291 }
292
293 private boolean checkVxlanPort(OvsdbNode ovsdb) {
294 // TODO change to use tunnel config
295 OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
296 try {
297 ovsdbClient.getPorts().stream()
298 .filter(p -> p.portName().value().equals(DEFAULT_TUNNEL))
299 .findFirst().get();
300 } catch (NoSuchElementException e) {
301 return false;
302 }
303 return true;
304 }
305
Hyunsun Moone9d75992015-09-15 22:39:16 -0700306 private class InternalDeviceListener implements DeviceListener {
307
308 @Override
309 public void event(DeviceEvent event) {
310 Device device = event.subject();
Hyunsun Moon9661d642015-09-23 13:24:35 -0700311 ConnectionHandler handler = (device.type() == SWITCH ? bridgeHandler : ovsdbHandler);
Hyunsun Moone9d75992015-09-15 22:39:16 -0700312
313 switch (event.type()) {
Hyunsun Moon9661d642015-09-23 13:24:35 -0700314 case DEVICE_ADDED:
315 eventExecutor.submit(() -> handler.connected(device));
316 break;
317 case DEVICE_AVAILABILITY_CHANGED:
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700318 if (deviceService.isAvailable(device.id())) {
319 eventExecutor.submit(() -> handler.connected(device));
320 } else {
321 eventExecutor.submit(() -> handler.disconnected(device));
322 }
Hyunsun Moon9661d642015-09-23 13:24:35 -0700323 break;
324 default:
325 break;
Hyunsun Moone9d75992015-09-15 22:39:16 -0700326 }
327 }
328 }
329
330 private class InternalHostListener implements HostListener {
331
332 @Override
333 public void event(HostEvent event) {
334 Host vm = event.subject();
335
336 switch (event.type()) {
337 case HOST_ADDED:
338 eventExecutor.submit(() -> vmHandler.connected(vm));
339 break;
340 case HOST_REMOVED:
341 eventExecutor.submit(() -> vmHandler.disconnected(vm));
342 break;
343 default:
344 break;
345 }
346 }
347 }
348
Hyunsun Moon9661d642015-09-23 13:24:35 -0700349 private class OvsdbHandler implements ConnectionHandler<Device> {
Hyunsun Moone9d75992015-09-15 22:39:16 -0700350
351 @Override
352 public void connected(Device device) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700353 log.info("Ovsdb {} is connected", device.id());
354
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700355 OvsdbNode ovsdb = getNode(device.id());
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700356 if (ovsdb != null) {
357 init(ovsdb);
358 }
Hyunsun Moone9d75992015-09-15 22:39:16 -0700359 }
360
361 @Override
362 public void disconnected(Device device) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700363 log.warn("Ovsdb {} is disconnected", device.id());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700364 }
365 }
366
367 private class BridgeHandler implements ConnectionHandler<Device> {
368
369 @Override
370 public void connected(Device device) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700371 log.info("Integration Bridge {} is detected", device.id());
372
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700373 OvsdbNode ovsdb;
374 try {
375 ovsdb = getNodes().stream()
376 .filter(node -> node.intBrId().equals(device.id()))
377 .findFirst().get();
378 } catch (NoSuchElementException e) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700379 log.warn("Couldn't find OVSDB associated with {}", device.id());
380 return;
381 }
382
Hyunsun Moonb525d5c2015-10-19 12:38:21 -0700383 if (!checkVxlanPort(ovsdb)) {
384 createVxlanPort(ovsdb);
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700385 }
Hyunsun Moone9d75992015-09-15 22:39:16 -0700386 }
387
388 @Override
389 public void disconnected(Device device) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700390 log.info("Integration Bridge {} is vanished", device.id());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700391 }
392 }
393
Hyunsun Moon9661d642015-09-23 13:24:35 -0700394 private class VmHandler implements ConnectionHandler<Host> {
Hyunsun Moone9d75992015-09-15 22:39:16 -0700395
396 @Override
397 public void connected(Host host) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700398 log.info("VM {} is detected", host.id());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700399 }
400
401 @Override
402 public void disconnected(Host host) {
Hyunsun Moon7dca9b32015-10-08 22:25:30 -0700403 log.info("VM {} is vanished", host.id());
Hyunsun Moone9d75992015-09-15 22:39:16 -0700404 }
405 }
406}