Committed by
Thomas Vachuska
[ONOS-2258]--OVSDB- The implementation of OvsdbController.
Change-Id: Ibaea6247668a7dc34a2cce2524555fcd85bbcbb1
Showing
2 changed files
with
449 additions
and
0 deletions
1 | +/* | ||
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 | + */ | ||
16 | +package org.onosproject.ovsdb.controller.impl; | ||
17 | + | ||
18 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
19 | + | ||
20 | +import java.math.BigInteger; | ||
21 | +import java.util.HashSet; | ||
22 | +import java.util.List; | ||
23 | +import java.util.Map; | ||
24 | +import java.util.Set; | ||
25 | +import java.util.concurrent.ConcurrentHashMap; | ||
26 | +import java.util.concurrent.CopyOnWriteArraySet; | ||
27 | +import java.util.concurrent.ExecutionException; | ||
28 | + | ||
29 | +import org.apache.felix.scr.annotations.Activate; | ||
30 | +import org.apache.felix.scr.annotations.Component; | ||
31 | +import org.apache.felix.scr.annotations.Deactivate; | ||
32 | +import org.apache.felix.scr.annotations.Service; | ||
33 | +import org.onlab.packet.IpAddress; | ||
34 | +import org.onlab.packet.MacAddress; | ||
35 | +import org.onosproject.ovsdb.controller.DefaultEventSubject; | ||
36 | +import org.onosproject.ovsdb.controller.EventSubject; | ||
37 | +import org.onosproject.ovsdb.controller.OvsdbClientService; | ||
38 | +import org.onosproject.ovsdb.controller.OvsdbConstant; | ||
39 | +import org.onosproject.ovsdb.controller.OvsdbController; | ||
40 | +import org.onosproject.ovsdb.controller.OvsdbDatapathId; | ||
41 | +import org.onosproject.ovsdb.controller.OvsdbEvent; | ||
42 | +import org.onosproject.ovsdb.controller.OvsdbEvent.Type; | ||
43 | +import org.onosproject.ovsdb.controller.OvsdbEventListener; | ||
44 | +import org.onosproject.ovsdb.controller.OvsdbIfaceId; | ||
45 | +import org.onosproject.ovsdb.controller.OvsdbNodeId; | ||
46 | +import org.onosproject.ovsdb.controller.OvsdbNodeListener; | ||
47 | +import org.onosproject.ovsdb.controller.OvsdbPortName; | ||
48 | +import org.onosproject.ovsdb.controller.OvsdbPortNumber; | ||
49 | +import org.onosproject.ovsdb.controller.OvsdbPortType; | ||
50 | +import org.onosproject.ovsdb.controller.driver.OvsdbAgent; | ||
51 | +import org.onosproject.ovsdb.rfc.jsonrpc.Callback; | ||
52 | +import org.onosproject.ovsdb.rfc.message.TableUpdate; | ||
53 | +import org.onosproject.ovsdb.rfc.message.TableUpdates; | ||
54 | +import org.onosproject.ovsdb.rfc.message.UpdateNotification; | ||
55 | +import org.onosproject.ovsdb.rfc.notation.OvsdbMap; | ||
56 | +import org.onosproject.ovsdb.rfc.notation.OvsdbSet; | ||
57 | +import org.onosproject.ovsdb.rfc.notation.Row; | ||
58 | +import org.onosproject.ovsdb.rfc.notation.UUID; | ||
59 | +import org.onosproject.ovsdb.rfc.schema.DatabaseSchema; | ||
60 | +import org.onosproject.ovsdb.rfc.table.Bridge; | ||
61 | +import org.onosproject.ovsdb.rfc.table.Interface; | ||
62 | +import org.onosproject.ovsdb.rfc.table.OvsdbTable; | ||
63 | +import org.onosproject.ovsdb.rfc.table.Port; | ||
64 | +import org.onosproject.ovsdb.rfc.table.TableGenerator; | ||
65 | +import org.onosproject.ovsdb.rfc.utils.FromJsonUtil; | ||
66 | +import org.osgi.service.component.ComponentContext; | ||
67 | +import org.slf4j.Logger; | ||
68 | +import org.slf4j.LoggerFactory; | ||
69 | + | ||
70 | +import com.fasterxml.jackson.databind.JsonNode; | ||
71 | + | ||
72 | +/** | ||
73 | + * The implementation of OvsdbController. | ||
74 | + */ | ||
75 | +@Component(immediate = true) | ||
76 | +@Service | ||
77 | +public class OvsdbControllerImpl implements OvsdbController { | ||
78 | + | ||
79 | + public static final Logger log = LoggerFactory | ||
80 | + .getLogger(OvsdbControllerImpl.class); | ||
81 | + | ||
82 | + protected ConcurrentHashMap<OvsdbNodeId, OvsdbClientService> ovsdbClients = | ||
83 | + new ConcurrentHashMap<OvsdbNodeId, OvsdbClientService>(); | ||
84 | + | ||
85 | + protected OvsdbAgent agent = new InternalOvsdbNodeAgent(); | ||
86 | + protected InternalMonitorCallBack updateCallback = new InternalMonitorCallBack(); | ||
87 | + | ||
88 | + protected Set<OvsdbNodeListener> ovsdbNodeListener = new CopyOnWriteArraySet<>(); | ||
89 | + protected Set<OvsdbEventListener> ovsdbEventListener = new CopyOnWriteArraySet<>(); | ||
90 | + | ||
91 | + protected ConcurrentHashMap<String, OvsdbClientService> requestNotification = | ||
92 | + new ConcurrentHashMap<String, OvsdbClientService>(); | ||
93 | + | ||
94 | + protected ConcurrentHashMap<String, String> requestDbName = new ConcurrentHashMap<String, String>(); | ||
95 | + | ||
96 | + private final Controller controller = new Controller(); | ||
97 | + | ||
98 | + @Activate | ||
99 | + public void activate(ComponentContext context) { | ||
100 | + controller.start(agent, updateCallback); | ||
101 | + log.info("Started"); | ||
102 | + } | ||
103 | + | ||
104 | + @Deactivate | ||
105 | + public void deactivate() { | ||
106 | + controller.stop(); | ||
107 | + log.info("Stoped"); | ||
108 | + } | ||
109 | + | ||
110 | + @Override | ||
111 | + public void addNodeListener(OvsdbNodeListener listener) { | ||
112 | + if (!ovsdbNodeListener.contains(listener)) { | ||
113 | + this.ovsdbNodeListener.add(listener); | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + @Override | ||
118 | + public void removeNodeListener(OvsdbNodeListener listener) { | ||
119 | + this.ovsdbNodeListener.remove(listener); | ||
120 | + } | ||
121 | + | ||
122 | + @Override | ||
123 | + public void addOvsdbEventListener(OvsdbEventListener listener) { | ||
124 | + if (!ovsdbEventListener.contains(listener)) { | ||
125 | + this.ovsdbEventListener.add(listener); | ||
126 | + } | ||
127 | + } | ||
128 | + | ||
129 | + @Override | ||
130 | + public void removeOvsdbEventListener(OvsdbEventListener listener) { | ||
131 | + this.ovsdbEventListener.remove(listener); | ||
132 | + } | ||
133 | + | ||
134 | + @Override | ||
135 | + public List<OvsdbNodeId> getNodeIds() { | ||
136 | + // TODO Auto-generated method stub | ||
137 | + return null; | ||
138 | + } | ||
139 | + | ||
140 | + @Override | ||
141 | + public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) { | ||
142 | + return ovsdbClients.get(nodeId); | ||
143 | + } | ||
144 | + | ||
145 | + /** | ||
146 | + * Implementation of an Ovsdb Agent which is responsible for keeping track | ||
147 | + * of connected node and the state in which they are. | ||
148 | + */ | ||
149 | + private class InternalOvsdbNodeAgent implements OvsdbAgent { | ||
150 | + @Override | ||
151 | + public void addConnectedNode(OvsdbNodeId nodeId, | ||
152 | + OvsdbClientService ovsdbClient) { | ||
153 | + | ||
154 | + if (ovsdbClients.get(nodeId) != null) { | ||
155 | + return; | ||
156 | + } else { | ||
157 | + ovsdbClients.put(nodeId, ovsdbClient); | ||
158 | + | ||
159 | + try { | ||
160 | + List<String> dbNames = ovsdbClient.listDbs().get(); | ||
161 | + for (String dbName : dbNames) { | ||
162 | + DatabaseSchema dbSchema; | ||
163 | + dbSchema = ovsdbClient.getOvsdbSchema(dbName).get(); | ||
164 | + | ||
165 | + log.debug("Begin to monitor tables"); | ||
166 | + String id = java.util.UUID.randomUUID().toString(); | ||
167 | + TableUpdates updates = ovsdbClient | ||
168 | + .monitorTables(dbName, id).get(); | ||
169 | + | ||
170 | + requestDbName.put(id, dbName); | ||
171 | + requestNotification.put(id, ovsdbClient); | ||
172 | + | ||
173 | + if (updates != null) { | ||
174 | + processTableUpdates(ovsdbClient, updates, | ||
175 | + dbSchema.name()); | ||
176 | + } | ||
177 | + } | ||
178 | + } catch (InterruptedException e) { | ||
179 | + log.warn("Interrupted while waiting to get message from ovsdb"); | ||
180 | + Thread.currentThread().interrupt(); | ||
181 | + } catch (ExecutionException e) { | ||
182 | + log.error("Exception thrown while to get message from ovsdb"); | ||
183 | + } | ||
184 | + | ||
185 | + log.debug("Add node to north"); | ||
186 | + for (OvsdbNodeListener l : ovsdbNodeListener) { | ||
187 | + l.nodeAdded(nodeId); | ||
188 | + } | ||
189 | + return; | ||
190 | + } | ||
191 | + } | ||
192 | + | ||
193 | + @Override | ||
194 | + public void removeConnectedNode(OvsdbNodeId nodeId) { | ||
195 | + ovsdbClients.remove(nodeId); | ||
196 | + log.debug("Node connection is removed"); | ||
197 | + for (OvsdbNodeListener l : ovsdbNodeListener) { | ||
198 | + l.nodeRemoved(nodeId); | ||
199 | + } | ||
200 | + } | ||
201 | + } | ||
202 | + | ||
203 | + /** | ||
204 | + * Processes table updates. | ||
205 | + * | ||
206 | + * @param clientService OvsdbClientService instance | ||
207 | + * @param updates TableUpdates instance | ||
208 | + * @param dbName ovsdb database name | ||
209 | + */ | ||
210 | + private void processTableUpdates(OvsdbClientService clientService, | ||
211 | + TableUpdates updates, String dbName) | ||
212 | + throws InterruptedException { | ||
213 | + checkNotNull(clientService, "OvsdbClientService is not null"); | ||
214 | + | ||
215 | + DatabaseSchema dbSchema = clientService.getDatabaseSchema(dbName); | ||
216 | + | ||
217 | + for (String tableName : updates.result().keySet()) { | ||
218 | + TableUpdate update = updates.result().get(tableName); | ||
219 | + for (UUID uuid : (Set<UUID>) update.rows().keySet()) { | ||
220 | + log.debug("Begin to process table updates uuid: {}, databaseName: {}, tableName: {}", | ||
221 | + uuid.value(), dbName, tableName); | ||
222 | + | ||
223 | + Row row = clientService.getRow(dbName, tableName, uuid.value()); | ||
224 | + clientService.updateOvsdbStore(dbName, tableName, uuid.value(), | ||
225 | + update.getNew(uuid)); | ||
226 | + if (update.getNew(uuid) != null) { | ||
227 | + boolean isNewRow = (row == null) ? true : false; | ||
228 | + if (isNewRow) { | ||
229 | + if (OvsdbConstant.PORT.equals(tableName)) { | ||
230 | + dispatchEvent(clientService, update.getNew(uuid), | ||
231 | + null, OvsdbEvent.Type.PORT_ADDED, | ||
232 | + dbSchema); | ||
233 | + } | ||
234 | + } | ||
235 | + } else if (update.getOld(uuid) != null) { | ||
236 | + clientService.removeRow(dbName, tableName, uuid.toString()); | ||
237 | + if (update.getOld(uuid) != null) { | ||
238 | + if (OvsdbConstant.PORT.equals(tableName)) { | ||
239 | + dispatchEvent(clientService, null, | ||
240 | + update.getOld(uuid), | ||
241 | + OvsdbEvent.Type.PORT_REMOVED, | ||
242 | + dbSchema); | ||
243 | + } | ||
244 | + } | ||
245 | + } | ||
246 | + } | ||
247 | + } | ||
248 | + } | ||
249 | + | ||
250 | + /** | ||
251 | + * Dispatches event to the north. | ||
252 | + * | ||
253 | + * @param clientService OvsdbClientService instance | ||
254 | + * @param newRow a new row | ||
255 | + * @param oldRow an old row | ||
256 | + * @param eventType type of event | ||
257 | + * @param dbSchema ovsdb database schema | ||
258 | + */ | ||
259 | + private void dispatchEvent(OvsdbClientService clientService, Row newRow, | ||
260 | + Row oldRow, Type eventType, | ||
261 | + DatabaseSchema dbSchema) { | ||
262 | + Port port = null; | ||
263 | + if (OvsdbEvent.Type.PORT_ADDED.equals(eventType)) { | ||
264 | + port = (Port) TableGenerator.getTable(dbSchema, newRow, | ||
265 | + OvsdbTable.PORT); | ||
266 | + } else if (OvsdbEvent.Type.PORT_REMOVED.equals(eventType)) { | ||
267 | + port = (Port) TableGenerator.getTable(dbSchema, oldRow, | ||
268 | + OvsdbTable.PORT); | ||
269 | + } | ||
270 | + if (port == null) { | ||
271 | + return; | ||
272 | + } | ||
273 | + | ||
274 | + long dpid = getDataPathid(clientService, dbSchema); | ||
275 | + OvsdbSet intfUuidSet = (OvsdbSet) port.getInterfacesColumn().data(); | ||
276 | + @SuppressWarnings({ "unchecked" }) | ||
277 | + Set<UUID> intfUuids = intfUuidSet.set(); | ||
278 | + for (UUID intfUuid : intfUuids) { | ||
279 | + Row intfRow = clientService | ||
280 | + .getRow(OvsdbConstant.DATABASENAME, "Interface", | ||
281 | + intfUuid.toString()); | ||
282 | + if (intfRow == null) { | ||
283 | + continue; | ||
284 | + } | ||
285 | + Interface intf = (Interface) TableGenerator | ||
286 | + .getTable(dbSchema, intfRow, OvsdbTable.INTERFACE); | ||
287 | + | ||
288 | + String portType = (String) intf.getTypeColumn().data(); | ||
289 | + long localPort = getOfPort(intf); | ||
290 | + String[] macAndIfaceId = getMacAndIfaceid(intf); | ||
291 | + if (macAndIfaceId == null) { | ||
292 | + return; | ||
293 | + } | ||
294 | + EventSubject eventSubject = new DefaultEventSubject( | ||
295 | + MacAddress | ||
296 | + .valueOf(macAndIfaceId[0]), | ||
297 | + new HashSet<IpAddress>(), | ||
298 | + new OvsdbPortName(port.getName()), | ||
299 | + new OvsdbPortNumber(localPort), | ||
300 | + new OvsdbDatapathId(Long.toString(dpid)), | ||
301 | + new OvsdbPortType(portType), | ||
302 | + new OvsdbIfaceId(macAndIfaceId[1])); | ||
303 | + for (OvsdbEventListener listener : ovsdbEventListener) { | ||
304 | + listener.handle(new OvsdbEvent<EventSubject>(eventType, | ||
305 | + eventSubject)); | ||
306 | + } | ||
307 | + | ||
308 | + } | ||
309 | + | ||
310 | + } | ||
311 | + | ||
312 | + /** | ||
313 | + * Gets mac and iface from the table Interface. | ||
314 | + * | ||
315 | + * @param intf Interface instance | ||
316 | + * @return attachedMac, ifaceid | ||
317 | + */ | ||
318 | + private String[] getMacAndIfaceid(Interface intf) { | ||
319 | + OvsdbMap ovsdbMap = (OvsdbMap) intf.getExternalIdsColumn().data(); | ||
320 | + @SuppressWarnings("unchecked") | ||
321 | + Map<String, String> externalIds = ovsdbMap.map(); | ||
322 | + if (externalIds == null) { | ||
323 | + log.warn("The external_ids is null"); | ||
324 | + return null; | ||
325 | + } | ||
326 | + | ||
327 | + String attachedMac = externalIds.get(OvsdbConstant.EXTERNAL_ID_VM_MAC); | ||
328 | + if (attachedMac == null) { | ||
329 | + log.warn("The attachedMac is null"); | ||
330 | + return null; | ||
331 | + } | ||
332 | + String ifaceid = externalIds | ||
333 | + .get(OvsdbConstant.EXTERNAL_ID_INTERFACE_ID); | ||
334 | + if (ifaceid == null) { | ||
335 | + log.warn("The ifaceid is null"); | ||
336 | + return null; | ||
337 | + } | ||
338 | + return new String[] {attachedMac, ifaceid}; | ||
339 | + } | ||
340 | + | ||
341 | + /** | ||
342 | + * Gets ofPorts number from table Interface. | ||
343 | + * | ||
344 | + * @param intf Interface instance | ||
345 | + * @return ofport the ofport number | ||
346 | + */ | ||
347 | + private long getOfPort(Interface intf) { | ||
348 | + OvsdbSet ovsdbSet = (OvsdbSet) intf.getOpenFlowPortColumn().data(); | ||
349 | + @SuppressWarnings("unchecked") | ||
350 | + Set<Long> ofPorts = ovsdbSet.set(); | ||
351 | + while (ofPorts == null || ofPorts.size() <= 0) { | ||
352 | + log.debug("The ofport is null in {}", intf.getName()); | ||
353 | + return 0; | ||
354 | + } | ||
355 | + return (long) ofPorts.toArray()[0]; | ||
356 | + } | ||
357 | + | ||
358 | + /** | ||
359 | + * Gets datapathid from table bridge. | ||
360 | + * | ||
361 | + * @param clientService OvsdbClientService instance | ||
362 | + * @param dbSchema ovsdb database schema | ||
363 | + * @return datapathid the bridge datapathid | ||
364 | + */ | ||
365 | + private long getDataPathid(OvsdbClientService clientService, | ||
366 | + DatabaseSchema dbSchema) { | ||
367 | + String bridgeUuid = clientService | ||
368 | + .getBridgeUuid(OvsdbConstant.INTEGRATION_BRIDGE); | ||
369 | + if (bridgeUuid == null) { | ||
370 | + log.debug("Unable to spot bridge uuid for {} in {}", | ||
371 | + OvsdbConstant.INTEGRATION_BRIDGE, clientService); | ||
372 | + return 0; | ||
373 | + } | ||
374 | + | ||
375 | + Row bridgeRow = clientService.getRow(OvsdbConstant.DATABASENAME, | ||
376 | + "Bridge", bridgeUuid); | ||
377 | + Bridge bridge = (Bridge) TableGenerator.getTable(dbSchema, bridgeRow, | ||
378 | + OvsdbTable.BRIDGE); | ||
379 | + OvsdbSet ovsdbSet = (OvsdbSet) bridge.getDatapathIdColumn().data(); | ||
380 | + @SuppressWarnings("unchecked") | ||
381 | + Set<String> dpids = ovsdbSet.set(); | ||
382 | + if (dpids == null || dpids.size() == 0) { | ||
383 | + return 0; | ||
384 | + } | ||
385 | + return stringToLong((String) dpids.toArray()[0]); | ||
386 | + } | ||
387 | + | ||
388 | + private long stringToLong(String values) { | ||
389 | + long value = (new BigInteger(values.replaceAll(":", ""), 16)) | ||
390 | + .longValue(); | ||
391 | + return value; | ||
392 | + } | ||
393 | + | ||
394 | + /** | ||
395 | + * Implementation of an Callback which is responsible for receiving request | ||
396 | + * infomation from ovsdb. | ||
397 | + */ | ||
398 | + private class InternalMonitorCallBack implements Callback { | ||
399 | + @Override | ||
400 | + public void update(UpdateNotification upadateNotification) { | ||
401 | + Object key = upadateNotification.context(); | ||
402 | + OvsdbClientService ovsdbClient = requestNotification.get(key); | ||
403 | + | ||
404 | + String dbName = requestDbName.get(key); | ||
405 | + JsonNode updatesJson = upadateNotification.tbUpdatesJsonNode(); | ||
406 | + DatabaseSchema dbSchema = ovsdbClient.getDatabaseSchema(dbName); | ||
407 | + TableUpdates updates = FromJsonUtil | ||
408 | + .jsonNodeToTableUpdates(updatesJson, dbSchema); | ||
409 | + try { | ||
410 | + processTableUpdates(ovsdbClient, updates, dbName); | ||
411 | + } catch (InterruptedException e) { | ||
412 | + log.warn("Interrupted while processing table updates"); | ||
413 | + Thread.currentThread().interrupt(); | ||
414 | + } | ||
415 | + } | ||
416 | + | ||
417 | + @Override | ||
418 | + public void locked(List<String> ids) { | ||
419 | + // TODO Auto-generated method stub | ||
420 | + } | ||
421 | + | ||
422 | + @Override | ||
423 | + public void stolen(List<String> ids) { | ||
424 | + // TODO Auto-generated method stub | ||
425 | + } | ||
426 | + | ||
427 | + } | ||
428 | + | ||
429 | +} |
1 | +/* | ||
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 | + */ | ||
16 | + | ||
17 | +/** | ||
18 | + * Implementation of the OVSDB controller IO subsystem. | ||
19 | + */ | ||
20 | +package org.onosproject.ovsdb.controller.impl; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment