blob: c2cbbf8ba81cc69c3f6968a07652aa0679892891 [file] [log] [blame]
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07001/*
2 * Copyright 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.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;
50import org.onosproject.ovsdb.rfc.notation.UUID;
51import 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;
70
71import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070072
73/**
74 * The implementation of OvsdbController.
75 */
76@Component(immediate = true)
77@Service
78public class OvsdbControllerImpl implements OvsdbController {
79
80 public static final Logger log = LoggerFactory
81 .getLogger(OvsdbControllerImpl.class);
82
83 protected ConcurrentHashMap<OvsdbNodeId, OvsdbClientService> ovsdbClients =
84 new ConcurrentHashMap<OvsdbNodeId, OvsdbClientService>();
85
86 protected OvsdbAgent agent = new InternalOvsdbNodeAgent();
87 protected InternalMonitorCallBack updateCallback = new InternalMonitorCallBack();
88
89 protected Set<OvsdbNodeListener> ovsdbNodeListener = new CopyOnWriteArraySet<>();
90 protected Set<OvsdbEventListener> ovsdbEventListener = new CopyOnWriteArraySet<>();
91
92 protected ConcurrentHashMap<String, OvsdbClientService> requestNotification =
93 new ConcurrentHashMap<String, OvsdbClientService>();
94
95 protected ConcurrentHashMap<String, String> requestDbName = new ConcurrentHashMap<String, String>();
96
BitOhenryf006ddd2015-11-17 13:25:41 +080097 private final Controller controller = new Controller();
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070098
99 @Activate
100 public void activate(ComponentContext context) {
101 controller.start(agent, updateCallback);
102 log.info("Started");
103 }
104
105 @Deactivate
106 public void deactivate() {
107 controller.stop();
108 log.info("Stoped");
109 }
110
111 @Override
112 public void addNodeListener(OvsdbNodeListener listener) {
113 if (!ovsdbNodeListener.contains(listener)) {
114 this.ovsdbNodeListener.add(listener);
115 }
116 }
117
118 @Override
119 public void removeNodeListener(OvsdbNodeListener listener) {
120 this.ovsdbNodeListener.remove(listener);
121 }
122
123 @Override
124 public void addOvsdbEventListener(OvsdbEventListener listener) {
125 if (!ovsdbEventListener.contains(listener)) {
126 this.ovsdbEventListener.add(listener);
127 }
128 }
129
130 @Override
131 public void removeOvsdbEventListener(OvsdbEventListener listener) {
132 this.ovsdbEventListener.remove(listener);
133 }
134
135 @Override
136 public List<OvsdbNodeId> getNodeIds() {
andreaed976a42015-10-05 14:38:25 -0700137 return ImmutableList.copyOf(ovsdbClients.keySet());
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700138 }
139
140 @Override
141 public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
142 return ovsdbClients.get(nodeId);
143 }
144
Hyunsun Moon5fb20a52015-09-25 17:02:33 -0700145 @Override
146 public void connect(IpAddress ip, TpPort port) {
147 controller.connect(ip, port);
148 }
149
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700150 /**
151 * Implementation of an Ovsdb Agent which is responsible for keeping track
152 * of connected node and the state in which they are.
153 */
154 private class InternalOvsdbNodeAgent implements OvsdbAgent {
155 @Override
156 public void addConnectedNode(OvsdbNodeId nodeId,
157 OvsdbClientService ovsdbClient) {
158
159 if (ovsdbClients.get(nodeId) != null) {
160 return;
161 } else {
162 ovsdbClients.put(nodeId, ovsdbClient);
163
164 try {
165 List<String> dbNames = ovsdbClient.listDbs().get();
166 for (String dbName : dbNames) {
167 DatabaseSchema dbSchema;
168 dbSchema = ovsdbClient.getOvsdbSchema(dbName).get();
169
170 log.debug("Begin to monitor tables");
171 String id = java.util.UUID.randomUUID().toString();
172 TableUpdates updates = ovsdbClient
173 .monitorTables(dbName, id).get();
174
175 requestDbName.put(id, dbName);
176 requestNotification.put(id, ovsdbClient);
177
178 if (updates != null) {
179 processTableUpdates(ovsdbClient, updates,
180 dbSchema.name());
181 }
182 }
183 } catch (InterruptedException e) {
184 log.warn("Interrupted while waiting to get message from ovsdb");
185 Thread.currentThread().interrupt();
186 } catch (ExecutionException e) {
187 log.error("Exception thrown while to get message from ovsdb");
188 }
189
190 log.debug("Add node to north");
191 for (OvsdbNodeListener l : ovsdbNodeListener) {
192 l.nodeAdded(nodeId);
193 }
194 return;
195 }
196 }
197
198 @Override
199 public void removeConnectedNode(OvsdbNodeId nodeId) {
200 ovsdbClients.remove(nodeId);
201 log.debug("Node connection is removed");
202 for (OvsdbNodeListener l : ovsdbNodeListener) {
203 l.nodeRemoved(nodeId);
204 }
205 }
206 }
207
208 /**
209 * Processes table updates.
210 *
211 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700212 * @param updates TableUpdates instance
213 * @param dbName ovsdb database name
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700214 */
215 private void processTableUpdates(OvsdbClientService clientService,
216 TableUpdates updates, String dbName)
217 throws InterruptedException {
218 checkNotNull(clientService, "OvsdbClientService is not null");
219
220 DatabaseSchema dbSchema = clientService.getDatabaseSchema(dbName);
221
222 for (String tableName : updates.result().keySet()) {
223 TableUpdate update = updates.result().get(tableName);
224 for (UUID uuid : (Set<UUID>) update.rows().keySet()) {
225 log.debug("Begin to process table updates uuid: {}, databaseName: {}, tableName: {}",
226 uuid.value(), dbName, tableName);
227
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700228 Row newRow = update.getNew(uuid);
229 if (newRow != null) {
230 clientService.updateOvsdbStore(dbName, tableName,
231 uuid.value(), newRow);
232
233 if (OvsdbConstant.INTERFACE.equals(tableName)) {
234 dispatchInterfaceEvent(clientService,
CNluciusa66c3972015-09-06 20:31:29 +0800235 newRow,
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700236 OvsdbEvent.Type.PORT_ADDED,
237 dbSchema);
238 }
239 } else if (update.getOld(uuid) != null) {
CNluciusa66c3972015-09-06 20:31:29 +0800240 if (OvsdbConstant.INTERFACE.equals(tableName)) {
241 Row row = clientService.getRow(OvsdbConstant.DATABASENAME, tableName, uuid.value());
242 dispatchInterfaceEvent(clientService,
243 row,
andreaed976a42015-10-05 14:38:25 -0700244 OvsdbEvent.Type.PORT_REMOVED,
245 dbSchema);
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700246 }
CNluciusa66c3972015-09-06 20:31:29 +0800247 clientService.removeRow(dbName, tableName, uuid.value());
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700248 }
249 }
250 }
251 }
252
253 /**
254 * Dispatches event to the north.
255 *
256 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700257 * @param newRow a new row
258 * @param oldRow an old row
259 * @param eventType type of event
260 * @param dbSchema ovsdb database schema
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700261 */
262 private void dispatchInterfaceEvent(OvsdbClientService clientService,
CNluciusa66c3972015-09-06 20:31:29 +0800263 Row row,
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700264 Type eventType,
265 DatabaseSchema dbSchema) {
266
267 long dpid = getDataPathid(clientService, dbSchema);
268 Interface intf = (Interface) TableGenerator
CNluciusa66c3972015-09-06 20:31:29 +0800269 .getTable(dbSchema, row, OvsdbTable.INTERFACE);
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700270 if (intf == null) {
271 return;
272 }
273
274 String portType = (String) intf.getTypeColumn().data();
275 long localPort = getOfPort(intf);
276 if (localPort < 0) {
277 return;
278 }
279 String[] macAndIfaceId = getMacAndIfaceid(intf);
280 if (macAndIfaceId == null) {
281 return;
282 }
283
284 EventSubject eventSubject = new DefaultEventSubject(MacAddress.valueOf(
andreaed976a42015-10-05 14:38:25 -0700285 macAndIfaceId[0]),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700286 new HashSet<IpAddress>(),
287 new OvsdbPortName(intf
andreaed976a42015-10-05 14:38:25 -0700288 .getName()),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700289 new OvsdbPortNumber(localPort),
290 new OvsdbDatapathId(Long
andreaed976a42015-10-05 14:38:25 -0700291 .toString(dpid)),
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700292 new OvsdbPortType(portType),
293 new OvsdbIfaceId(macAndIfaceId[1]));
294 for (OvsdbEventListener listener : ovsdbEventListener) {
295 listener.handle(new OvsdbEvent<EventSubject>(eventType,
296 eventSubject));
297 }
298 }
299
300 /**
301 * Gets mac and iface from the table Interface.
302 *
303 * @param intf Interface instance
304 * @return attachedMac, ifaceid
305 */
306 private String[] getMacAndIfaceid(Interface intf) {
307 OvsdbMap ovsdbMap = (OvsdbMap) intf.getExternalIdsColumn().data();
308 @SuppressWarnings("unchecked")
309 Map<String, String> externalIds = ovsdbMap.map();
310 if (externalIds == null) {
311 log.warn("The external_ids is null");
312 return null;
313 }
314
315 String attachedMac = externalIds.get(OvsdbConstant.EXTERNAL_ID_VM_MAC);
316 if (attachedMac == null) {
andreaed976a42015-10-05 14:38:25 -0700317 log.debug("The attachedMac is null"); //FIXME why always null?
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700318 return null;
319 }
320 String ifaceid = externalIds
321 .get(OvsdbConstant.EXTERNAL_ID_INTERFACE_ID);
322 if (ifaceid == null) {
323 log.warn("The ifaceid is null");
324 return null;
325 }
andreaed976a42015-10-05 14:38:25 -0700326 return new String[]{attachedMac, ifaceid};
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700327 }
328
329 /**
330 * Gets ofPorts number from table Interface.
331 *
332 * @param intf Interface instance
333 * @return ofport the ofport number
334 */
335 private long getOfPort(Interface intf) {
336 OvsdbSet ofPortSet = (OvsdbSet) intf.getOpenFlowPortColumn().data();
337 @SuppressWarnings("unchecked")
338 Set<Integer> ofPorts = ofPortSet.set();
339 while (ofPorts == null || ofPorts.size() <= 0) {
340 log.debug("The ofport is null in {}", intf.getName());
341 return -1;
342 }
343 Iterator<Integer> it = ofPorts.iterator();
344 return Long.parseLong(it.next().toString());
345 }
346
347 /**
348 * Gets datapathid from table bridge.
349 *
350 * @param clientService OvsdbClientService instance
andreaed976a42015-10-05 14:38:25 -0700351 * @param dbSchema ovsdb database schema
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700352 * @return datapathid the bridge datapathid
353 */
354 private long getDataPathid(OvsdbClientService clientService,
355 DatabaseSchema dbSchema) {
356 String bridgeUuid = clientService
357 .getBridgeUuid(OvsdbConstant.INTEGRATION_BRIDGE);
358 if (bridgeUuid == null) {
359 log.debug("Unable to spot bridge uuid for {} in {}",
360 OvsdbConstant.INTEGRATION_BRIDGE, clientService);
361 return 0;
362 }
363
364 Row bridgeRow = clientService.getRow(OvsdbConstant.DATABASENAME,
365 "Bridge", bridgeUuid);
366 Bridge bridge = (Bridge) TableGenerator.getTable(dbSchema, bridgeRow,
367 OvsdbTable.BRIDGE);
368 OvsdbSet dpidSet = (OvsdbSet) bridge.getDatapathIdColumn().data();
369 @SuppressWarnings("unchecked")
370 Set<String> dpids = dpidSet.set();
371 if (dpids == null || dpids.size() == 0) {
372 return 0;
373 }
374 return stringToLong((String) dpids.toArray()[0]);
375 }
376
377 private long stringToLong(String values) {
378 long value = (new BigInteger(values.replaceAll(":", ""), 16))
379 .longValue();
380 return value;
381 }
382
383 /**
384 * Implementation of an Callback which is responsible for receiving request
385 * infomation from ovsdb.
386 */
387 private class InternalMonitorCallBack implements Callback {
388 @Override
389 public void update(UpdateNotification updateNotification) {
390 Object key = updateNotification.jsonValue();
391 OvsdbClientService ovsdbClient = requestNotification.get(key);
392
393 String dbName = requestDbName.get(key);
394 JsonNode updatesJson = updateNotification.tbUpdatesJsonNode();
395 DatabaseSchema dbSchema = ovsdbClient.getDatabaseSchema(dbName);
396 TableUpdates updates = FromJsonUtil
397 .jsonNodeToTableUpdates(updatesJson, dbSchema);
398 try {
399 processTableUpdates(ovsdbClient, updates, dbName);
400 } catch (InterruptedException e) {
401 log.warn("Interrupted while processing table updates");
402 Thread.currentThread().interrupt();
403 }
404 }
405
406 @Override
407 public void locked(List<String> ids) {
408 // TODO Auto-generated method stub
409 }
410
411 @Override
412 public void stolen(List<String> ids) {
413 // TODO Auto-generated method stub
414 }
415
416 }
417
418}