blob: 0349f842cf1a98bf8f5b1a9ea9ac500014258783 [file] [log] [blame]
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07003 *
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.ovsdb.controller.impl;
17
andreaed976a42015-10-05 14:38:25 -070018import com.fasterxml.jackson.databind.JsonNode;
19import com.google.common.collect.ImmutableList;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -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.Service;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.MacAddress;
Hyunsun Moon5fb20a52015-09-25 17:02:33 -070026import org.onlab.packet.TpPort;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070027import org.onosproject.ovsdb.controller.DefaultEventSubject;
28import org.onosproject.ovsdb.controller.EventSubject;
29import org.onosproject.ovsdb.controller.OvsdbClientService;
30import org.onosproject.ovsdb.controller.OvsdbConstant;
31import org.onosproject.ovsdb.controller.OvsdbController;
32import org.onosproject.ovsdb.controller.OvsdbDatapathId;
33import org.onosproject.ovsdb.controller.OvsdbEvent;
34import org.onosproject.ovsdb.controller.OvsdbEvent.Type;
35import org.onosproject.ovsdb.controller.OvsdbEventListener;
36import org.onosproject.ovsdb.controller.OvsdbIfaceId;
37import org.onosproject.ovsdb.controller.OvsdbNodeId;
38import org.onosproject.ovsdb.controller.OvsdbNodeListener;
39import org.onosproject.ovsdb.controller.OvsdbPortName;
40import org.onosproject.ovsdb.controller.OvsdbPortNumber;
41import org.onosproject.ovsdb.controller.OvsdbPortType;
42import org.onosproject.ovsdb.controller.driver.OvsdbAgent;
43import org.onosproject.ovsdb.rfc.jsonrpc.Callback;
44import org.onosproject.ovsdb.rfc.message.TableUpdate;
45import org.onosproject.ovsdb.rfc.message.TableUpdates;
46import org.onosproject.ovsdb.rfc.message.UpdateNotification;
47import org.onosproject.ovsdb.rfc.notation.OvsdbMap;
48import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
49import org.onosproject.ovsdb.rfc.notation.Row;
Jonathan Hart51539b82015-10-29 09:53:04 -070050import org.onosproject.ovsdb.rfc.notation.Uuid;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070051import org.onosproject.ovsdb.rfc.schema.DatabaseSchema;
52import org.onosproject.ovsdb.rfc.table.Bridge;
53import org.onosproject.ovsdb.rfc.table.Interface;
54import org.onosproject.ovsdb.rfc.table.OvsdbTable;
55import org.onosproject.ovsdb.rfc.table.TableGenerator;
56import org.onosproject.ovsdb.rfc.utils.FromJsonUtil;
57import org.osgi.service.component.ComponentContext;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
andreaed976a42015-10-05 14:38:25 -070061import java.math.BigInteger;
62import java.util.HashSet;
63import java.util.Iterator;
64import java.util.List;
65import java.util.Map;
66import java.util.Set;
67import java.util.concurrent.ConcurrentHashMap;
68import java.util.concurrent.CopyOnWriteArraySet;
69import java.util.concurrent.ExecutionException;
jiangruibce80652017-10-17 14:35:42 +080070import java.util.concurrent.TimeUnit;
71import java.util.concurrent.TimeoutException;
jaegonkim1af0ae52017-01-01 10:46:55 +090072import java.util.function.Consumer;
andreaed976a42015-10-05 14:38:25 -070073
74import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070075
76/**
77 * The implementation of OvsdbController.
78 */
79@Component(immediate = true)
80@Service
81public class OvsdbControllerImpl implements OvsdbController {
82
83 public static final Logger log = LoggerFactory
84 .getLogger(OvsdbControllerImpl.class);
jiangruibce80652017-10-17 14:35:42 +080085 private static final long DEFAULT_OVSDB_RPC_TIMEOUT = 3000;
86 private final Controller controller = new Controller();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070087 protected ConcurrentHashMap<OvsdbNodeId, OvsdbClientService> ovsdbClients =
88 new ConcurrentHashMap<OvsdbNodeId, OvsdbClientService>();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070089 protected OvsdbAgent agent = new InternalOvsdbNodeAgent();
90 protected InternalMonitorCallBack updateCallback = new InternalMonitorCallBack();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070091 protected Set<OvsdbNodeListener> ovsdbNodeListener = new CopyOnWriteArraySet<>();
92 protected Set<OvsdbEventListener> ovsdbEventListener = new CopyOnWriteArraySet<>();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070093 protected ConcurrentHashMap<String, OvsdbClientService> requestNotification =
94 new ConcurrentHashMap<String, OvsdbClientService>();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070095 protected ConcurrentHashMap<String, String> requestDbName = new ConcurrentHashMap<String, String>();
96
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070097 @Activate
98 public void activate(ComponentContext context) {
99 controller.start(agent, updateCallback);
100 log.info("Started");
101 }
102
103 @Deactivate
104 public void deactivate() {
105 controller.stop();
106 log.info("Stoped");
107 }
108
109 @Override
110 public void addNodeListener(OvsdbNodeListener listener) {
111 if (!ovsdbNodeListener.contains(listener)) {
112 this.ovsdbNodeListener.add(listener);
113 }
114 }
115
116 @Override
117 public void removeNodeListener(OvsdbNodeListener listener) {
118 this.ovsdbNodeListener.remove(listener);
119 }
120
121 @Override
122 public void addOvsdbEventListener(OvsdbEventListener listener) {
123 if (!ovsdbEventListener.contains(listener)) {
124 this.ovsdbEventListener.add(listener);
125 }
126 }
127
128 @Override
129 public void removeOvsdbEventListener(OvsdbEventListener listener) {
130 this.ovsdbEventListener.remove(listener);
131 }
132
133 @Override
134 public List<OvsdbNodeId> getNodeIds() {
andreaed976a42015-10-05 14:38:25 -0700135 return ImmutableList.copyOf(ovsdbClients.keySet());
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700136 }
137
138 @Override
139 public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
140 return ovsdbClients.get(nodeId);
141 }
142
Hyunsun Moon5fb20a52015-09-25 17:02:33 -0700143 @Override
144 public void connect(IpAddress ip, TpPort port) {
145 controller.connect(ip, port);
146 }
147
jaegonkim1af0ae52017-01-01 10:46:55 +0900148 @Override
149 public void connect(IpAddress ip, TpPort port, Consumer<Exception> failhandler) {
150 controller.connect(ip, port, failhandler);
151 }
152
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700153 /**
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700154 * Processes table updates.
155 *
156 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700157 * @param updates TableUpdates instance
158 * @param dbName ovsdb database name
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700159 */
160 private void processTableUpdates(OvsdbClientService clientService,
161 TableUpdates updates, String dbName)
162 throws InterruptedException {
163 checkNotNull(clientService, "OvsdbClientService is not null");
164
165 DatabaseSchema dbSchema = clientService.getDatabaseSchema(dbName);
166
167 for (String tableName : updates.result().keySet()) {
168 TableUpdate update = updates.result().get(tableName);
Jonathan Hart51539b82015-10-29 09:53:04 -0700169 for (Uuid uuid : (Set<Uuid>) update.rows().keySet()) {
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700170 log.debug("Begin to process table updates uuid: {}, databaseName: {}, tableName: {}",
171 uuid.value(), dbName, tableName);
172
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700173 Row newRow = update.getNew(uuid);
174 if (newRow != null) {
175 clientService.updateOvsdbStore(dbName, tableName,
176 uuid.value(), newRow);
177
178 if (OvsdbConstant.INTERFACE.equals(tableName)) {
179 dispatchInterfaceEvent(clientService,
CNluciusa66c3972015-09-06 20:31:29 +0800180 newRow,
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700181 OvsdbEvent.Type.PORT_ADDED,
182 dbSchema);
183 }
184 } else if (update.getOld(uuid) != null) {
CNluciusa66c3972015-09-06 20:31:29 +0800185 if (OvsdbConstant.INTERFACE.equals(tableName)) {
186 Row row = clientService.getRow(OvsdbConstant.DATABASENAME, tableName, uuid.value());
187 dispatchInterfaceEvent(clientService,
188 row,
andreaed976a42015-10-05 14:38:25 -0700189 OvsdbEvent.Type.PORT_REMOVED,
190 dbSchema);
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700191 }
CNluciusa66c3972015-09-06 20:31:29 +0800192 clientService.removeRow(dbName, tableName, uuid.value());
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700193 }
194 }
195 }
196 }
197
198 /**
199 * Dispatches event to the north.
200 *
201 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700202 * @param newRow a new row
203 * @param oldRow an old row
204 * @param eventType type of event
205 * @param dbSchema ovsdb database schema
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700206 */
207 private void dispatchInterfaceEvent(OvsdbClientService clientService,
CNluciusa66c3972015-09-06 20:31:29 +0800208 Row row,
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700209 Type eventType,
210 DatabaseSchema dbSchema) {
211
212 long dpid = getDataPathid(clientService, dbSchema);
213 Interface intf = (Interface) TableGenerator
CNluciusa66c3972015-09-06 20:31:29 +0800214 .getTable(dbSchema, row, OvsdbTable.INTERFACE);
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700215 if (intf == null) {
216 return;
217 }
218
219 String portType = (String) intf.getTypeColumn().data();
220 long localPort = getOfPort(intf);
221 if (localPort < 0) {
222 return;
223 }
224 String[] macAndIfaceId = getMacAndIfaceid(intf);
225 if (macAndIfaceId == null) {
226 return;
227 }
228
229 EventSubject eventSubject = new DefaultEventSubject(MacAddress.valueOf(
andreaed976a42015-10-05 14:38:25 -0700230 macAndIfaceId[0]),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700231 new HashSet<IpAddress>(),
232 new OvsdbPortName(intf
andreaed976a42015-10-05 14:38:25 -0700233 .getName()),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700234 new OvsdbPortNumber(localPort),
235 new OvsdbDatapathId(Long
andreaed976a42015-10-05 14:38:25 -0700236 .toString(dpid)),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700237 new OvsdbPortType(portType),
238 new OvsdbIfaceId(macAndIfaceId[1]));
239 for (OvsdbEventListener listener : ovsdbEventListener) {
240 listener.handle(new OvsdbEvent<EventSubject>(eventType,
241 eventSubject));
242 }
243 }
244
245 /**
246 * Gets mac and iface from the table Interface.
247 *
248 * @param intf Interface instance
249 * @return attachedMac, ifaceid
250 */
251 private String[] getMacAndIfaceid(Interface intf) {
252 OvsdbMap ovsdbMap = (OvsdbMap) intf.getExternalIdsColumn().data();
253 @SuppressWarnings("unchecked")
254 Map<String, String> externalIds = ovsdbMap.map();
255 if (externalIds == null) {
256 log.warn("The external_ids is null");
257 return null;
258 }
259
260 String attachedMac = externalIds.get(OvsdbConstant.EXTERNAL_ID_VM_MAC);
261 if (attachedMac == null) {
andreaed976a42015-10-05 14:38:25 -0700262 log.debug("The attachedMac is null"); //FIXME why always null?
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700263 return null;
264 }
265 String ifaceid = externalIds
266 .get(OvsdbConstant.EXTERNAL_ID_INTERFACE_ID);
267 if (ifaceid == null) {
268 log.warn("The ifaceid is null");
269 return null;
270 }
andreaed976a42015-10-05 14:38:25 -0700271 return new String[]{attachedMac, ifaceid};
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700272 }
273
274 /**
275 * Gets ofPorts number from table Interface.
276 *
277 * @param intf Interface instance
278 * @return ofport the ofport number
279 */
280 private long getOfPort(Interface intf) {
281 OvsdbSet ofPortSet = (OvsdbSet) intf.getOpenFlowPortColumn().data();
282 @SuppressWarnings("unchecked")
283 Set<Integer> ofPorts = ofPortSet.set();
Jon Hallb1f4e0f2017-02-22 13:36:16 -0800284 if (ofPorts == null || ofPorts.isEmpty()) {
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700285 log.debug("The ofport is null in {}", intf.getName());
286 return -1;
287 }
288 Iterator<Integer> it = ofPorts.iterator();
289 return Long.parseLong(it.next().toString());
290 }
291
292 /**
293 * Gets datapathid from table bridge.
294 *
295 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700296 * @param dbSchema ovsdb database schema
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700297 * @return datapathid the bridge datapathid
298 */
299 private long getDataPathid(OvsdbClientService clientService,
300 DatabaseSchema dbSchema) {
301 String bridgeUuid = clientService
302 .getBridgeUuid(OvsdbConstant.INTEGRATION_BRIDGE);
303 if (bridgeUuid == null) {
304 log.debug("Unable to spot bridge uuid for {} in {}",
305 OvsdbConstant.INTEGRATION_BRIDGE, clientService);
306 return 0;
307 }
308
309 Row bridgeRow = clientService.getRow(OvsdbConstant.DATABASENAME,
310 "Bridge", bridgeUuid);
311 Bridge bridge = (Bridge) TableGenerator.getTable(dbSchema, bridgeRow,
312 OvsdbTable.BRIDGE);
313 OvsdbSet dpidSet = (OvsdbSet) bridge.getDatapathIdColumn().data();
314 @SuppressWarnings("unchecked")
315 Set<String> dpids = dpidSet.set();
Jon Hallcbd1b392017-01-18 20:15:44 -0800316 if (dpids == null || dpids.isEmpty()) {
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700317 return 0;
318 }
319 return stringToLong((String) dpids.toArray()[0]);
320 }
321
322 private long stringToLong(String values) {
323 long value = (new BigInteger(values.replaceAll(":", ""), 16))
324 .longValue();
325 return value;
326 }
327
328 /**
jiangruibce80652017-10-17 14:35:42 +0800329 * Implementation of an Ovsdb Agent which is responsible for keeping track
330 * of connected node and the state in which they are.
331 */
332 private class InternalOvsdbNodeAgent implements OvsdbAgent {
333 @Override
334 public void addConnectedNode(OvsdbNodeId nodeId,
335 OvsdbClientService ovsdbClient) {
336
337 if (ovsdbClients.get(nodeId) != null) {
338 ovsdbClient.disconnect();
339 return;
340 } else {
341
342 try {
343 List<String> dbNames = ovsdbClient.listDbs().get(DEFAULT_OVSDB_RPC_TIMEOUT, TimeUnit.MILLISECONDS);
344 for (String dbName : dbNames) {
345 DatabaseSchema dbSchema;
346 dbSchema = ovsdbClient.getOvsdbSchema(dbName)
347 .get(DEFAULT_OVSDB_RPC_TIMEOUT, TimeUnit.MILLISECONDS);
348
349 log.debug("Begin to monitor tables");
350 String id = java.util.UUID.randomUUID().toString();
351 TableUpdates updates = ovsdbClient
352 .monitorTables(dbName, id).get(DEFAULT_OVSDB_RPC_TIMEOUT, TimeUnit.MILLISECONDS);
353
354 requestDbName.put(id, dbName);
355 requestNotification.put(id, ovsdbClient);
356
357 if (updates != null) {
358 processTableUpdates(ovsdbClient, updates,
359 dbSchema.name());
360 }
361 }
362 } catch (InterruptedException e) {
363 log.warn("Interrupted while waiting to get message from ovsdb");
364 Thread.currentThread().interrupt();
365 return;
366 } catch (ExecutionException e) {
367 log.error("Exception thrown while to get message from ovsdb");
368 ovsdbClient.disconnect();
369 return;
370 } catch (TimeoutException e) {
371 log.error("TimeoutException thrown while to get message from ovsdb");
372 ovsdbClient.disconnect();
373 return;
374 }
375 ovsdbClients.put(nodeId, ovsdbClient);
376
377 log.debug("Add node to north");
378 for (OvsdbNodeListener l : ovsdbNodeListener) {
379 l.nodeAdded(nodeId);
380 }
381 return;
382 }
383 }
384
385 @Override
386 public void removeConnectedNode(OvsdbNodeId nodeId) {
387 ovsdbClients.remove(nodeId);
388 log.debug("Node connection is removed");
389 for (OvsdbNodeListener l : ovsdbNodeListener) {
390 l.nodeRemoved(nodeId);
391 }
392 }
393 }
394
395 /**
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700396 * Implementation of an Callback which is responsible for receiving request
397 * infomation from ovsdb.
398 */
399 private class InternalMonitorCallBack implements Callback {
400 @Override
401 public void update(UpdateNotification updateNotification) {
402 Object key = updateNotification.jsonValue();
403 OvsdbClientService ovsdbClient = requestNotification.get(key);
404
405 String dbName = requestDbName.get(key);
406 JsonNode updatesJson = updateNotification.tbUpdatesJsonNode();
407 DatabaseSchema dbSchema = ovsdbClient.getDatabaseSchema(dbName);
408 TableUpdates updates = FromJsonUtil
409 .jsonNodeToTableUpdates(updatesJson, dbSchema);
410 try {
411 processTableUpdates(ovsdbClient, updates, dbName);
412 } catch (InterruptedException e) {
413 log.warn("Interrupted while processing table updates");
414 Thread.currentThread().interrupt();
415 }
416 }
417
418 @Override
419 public void locked(List<String> ids) {
420 // TODO Auto-generated method stub
421 }
422
423 @Override
424 public void stolen(List<String> ids) {
425 // TODO Auto-generated method stub
426 }
427
428 }
429
430}