2 * Copyright (C) 2013 Red Hat, Inc.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 * Authors : Madhu Venugopal, Brent Salisbury, Keith Burns
10 package org.opendaylight.ovsdb.plugin.impl;
12 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.AbstractMap;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.ExecutionException;
25 import org.eclipse.osgi.framework.console.CommandProvider;
26 import org.opendaylight.controller.sal.core.Node;
27 import org.opendaylight.controller.sal.utils.Status;
28 import org.opendaylight.controller.sal.utils.StatusCode;
29 import org.opendaylight.ovsdb.lib.OvsdbClient;
30 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
31 import org.opendaylight.ovsdb.lib.notation.Column;
32 import org.opendaylight.ovsdb.lib.notation.Mutator;
33 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
34 import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
35 import org.opendaylight.ovsdb.lib.notation.Row;
36 import org.opendaylight.ovsdb.lib.notation.UUID;
37 import org.opendaylight.ovsdb.lib.operations.Insert;
38 import org.opendaylight.ovsdb.lib.operations.Operation;
39 import org.opendaylight.ovsdb.lib.operations.OperationResult;
40 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
41 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
42 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
43 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
44 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
45 import org.opendaylight.ovsdb.lib.schema.TableSchema;
46 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
47 import org.opendaylight.ovsdb.plugin.api.Connection;
48 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
49 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
50 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
51 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryService;
52 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
53 import org.opendaylight.ovsdb.plugin.error.OvsdbPluginException;
54 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
55 import org.opendaylight.ovsdb.schema.openvswitch.Controller;
56 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
57 import org.opendaylight.ovsdb.schema.openvswitch.Port;
58 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
60 import org.osgi.framework.BundleContext;
61 import org.osgi.framework.FrameworkUtil;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 import com.fasterxml.jackson.databind.node.ObjectNode;
66 import com.google.common.collect.Maps;
67 import com.google.common.collect.Sets;
68 import com.google.common.util.concurrent.ListenableFuture;
70 public class ConfigurationServiceImpl implements OvsdbConfigurationService
72 private static final Logger logger = LoggerFactory
73 .getLogger(ConfigurationServiceImpl.class);
75 OvsdbConnectionService connectionService;
76 OvsdbInventoryService ovsdbInventoryService;
77 protected static final String OPENFLOW_13 = "1.3";
83 * Function called by the dependency manager when at least one dependency
84 * become unsatisfied or when the component is shutting down because for
85 * example bundle is being stopped.
92 * Function called by dependency manager after "init ()" is called and after
93 * the services provided by the class are registered in the service registry
97 registerWithOSGIConsole();
100 private void registerWithOSGIConsole() {
101 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
103 bundleContext.registerService(CommandProvider.class.getName(), this,
108 * Function called by the dependency manager before the services exported by
109 * the component are unregistered, this will be followed by a "destroy ()"
116 public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
117 this.connectionService = connectionService;
120 public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
121 if (this.connectionService == connectionService) {
122 this.connectionService = null;
126 public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
127 this.ovsdbInventoryService = ovsdbInventoryService;
130 public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
131 if (this.ovsdbInventoryService == ovsdbInventoryService) {
132 this.ovsdbInventoryService = null;
136 private Connection getConnection (Node node) {
137 Connection connection = connectionService.getConnection(node);
138 if (connection == null || !connection.getClient().isActive()) {
145 * There are a few Open_vSwitch schema specific special case handling to be done for
146 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
149 private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
150 String tableName, String uuid, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
151 Port port = client.getTypedRowWrapper(Port.class, null);
152 if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
153 port = client.getTypedRowWrapper(Port.class, row);
154 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
155 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
156 ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
157 String namedUuid = "Special_"+tableName;
158 List<Operation> priorOperations = transactionBuilder.getOperations();
159 Insert portOperation = (Insert)priorOperations.get(0);
160 portOperation.value(columnSchema, new UUID(namedUuid));
162 Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
163 List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
164 columns.add(nameColumn);
165 Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
166 this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
171 * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
172 * the parent table for the newly inserted Child.
173 * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
175 private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
176 String parentTable, String parentUuid, String parentColumn, String namedUuid,
177 Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
178 this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
179 namedUuid, row, transactionBuilder);
181 * There are a few Open_vSwitch schema specific special case handling to be done for
182 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
185 handleSpecialInsertCase(client, databaseName, childTable, namedUuid, row, transactionBuilder);
189 * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
190 * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
191 * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
194 public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
195 if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) return null;
196 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
197 if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
198 Connection connection = connectionService.getConnection(node);
199 OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
200 ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
201 if (row == null || row.size() == 0) return null;
202 return (String)row.keySet().toArray()[0];
208 * Though this is a New API that takes in Row object, this still is considered a
209 * Deprecated call because of the assumption with a Single Row insertion.
210 * An ideal insertRow must be able to take in multiple Rows, which includes the
211 * Row being inserted in one Table and other Rows that needs mutate in other Tables.
215 public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
216 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
217 if (parentColumn == null) {
218 parentColumn = new String[]{null, null};
221 Connection connection = connectionService.getConnection(node);
222 OvsdbClient client = connection.getClient();
224 if (parentUuid == null) {
225 parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
227 logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
228 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
230 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
231 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
233 String namedUuid = "Transaction_"+ tableName;
234 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
235 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
236 row, transactionBuilder);
238 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
239 List<OperationResult> operationResults;
241 operationResults = results.get();
242 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
243 return new StatusWithUuid(StatusCode.INTERNALERROR);
245 for (OperationResult result : operationResults) {
246 if (result.getError() != null) {
247 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
250 UUID uuid = operationResults.get(0).getUuid();
251 return new StatusWithUuid(StatusCode.SUCCESS, uuid);
252 } catch (InterruptedException | ExecutionException e) {
253 // TODO Auto-generated catch block
254 return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
261 public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
262 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
263 Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
264 return new StatusWithUuid(StatusCode.SUCCESS);
267 private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
268 String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
269 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
270 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
272 if (parentColumn != null) {
273 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
274 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
276 .add(op.mutate(parentTableSchema)
277 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
278 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
282 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
283 transactionBuilder.add(op.delete(childTableSchema)
284 .where(_uuid.opEqual(new UUID(uuid)))
290 public Status deleteRow(Node node, String tableName, String uuid) {
291 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
292 Connection connection = connectionService.getConnection(node);
293 OvsdbClient client = connection.getClient();
295 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
296 if (parentColumn == null) {
297 parentColumn = new String[]{null, null};
300 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
301 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
303 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
304 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
305 this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
306 parentColumn[0], parentColumn[1], uuid, transactionBuilder);
308 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
309 List<OperationResult> operationResults;
311 operationResults = results.get();
312 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
313 return new StatusWithUuid(StatusCode.INTERNALERROR);
315 for (OperationResult result : operationResults) {
316 if (result.getError() != null) {
317 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
320 } catch (InterruptedException | ExecutionException e) {
321 logger.error("Error in deleteRow() {} {}", node, tableName, e);
324 return new Status(StatusCode.SUCCESS);
329 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
330 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
336 public Row getRow(Node node, String tableName, String uuid) {
337 Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
338 if (ovsTable == null) return null;
339 return ovsTable.get(uuid);
344 public List<String> getTables(Node node) {
345 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
348 private InetAddress getControllerIPAddress(Connection connection) {
349 InetAddress controllerIP = null;
351 String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
353 if (addressString != null) {
355 controllerIP = InetAddress.getByName(addressString);
356 if (controllerIP != null) {
359 } catch (UnknownHostException e) {
360 logger.error("Host {} is invalid", addressString);
364 addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
366 if (addressString != null) {
368 controllerIP = InetAddress.getByName(addressString);
369 if (controllerIP != null) {
372 } catch (UnknownHostException e) {
373 logger.error("Host {} is invalid", addressString);
378 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
380 } catch (Exception e) {
381 logger.debug("Invalid connection provided to getControllerIPAddresses", e);
386 private short getControllerOFPort() {
387 Short defaultOpenFlowPort = 6633;
388 Short openFlowPort = defaultOpenFlowPort;
389 String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
390 if (portString != null) {
392 openFlowPort = Short.decode(portString).shortValue();
393 } catch (NumberFormatException e) {
394 logger.warn("Invalid port:{}, use default({})", portString,
401 private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
402 ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
405 for (Map.Entry<String, Row> entry : rows.entrySet()) {
406 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
407 Column<GenericTableSchema, String> column = currController.getTargetColumn();
408 String currTarget = column.getData();
409 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
410 return currController.getUuid();
418 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
419 Connection connection = this.getConnection(node);
420 if (connection == null) {
424 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
426 Status updateOperationStatus = null;
428 OvsdbSet<String> protocols = new OvsdbSet<String>();
430 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
435 protocols.add("OpenFlow13");
438 bridge.setProtocols(protocols);
439 updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
440 null, bridgeUUID, bridge.getRow());
441 logger.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
442 protocols.toArray()[0],updateOperationStatus);
444 } catch (SchemaVersionMismatchException e){
445 logger.debug(e.toString());
448 // If we fail to update the protocols
449 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
450 return updateOperationStatus.isSuccess();
453 Status status = null;
454 UUID currControllerUuid = null;
455 InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
456 short ofControllerPort = getControllerOFPort();
457 String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
458 Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
459 newController.setTarget(newControllerTarget);
460 final String controllerTableName = newController.getSchema().getName();
462 currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
464 if (currControllerUuid != null) {
465 bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
466 bridge.setController(Sets.newHashSet(currControllerUuid));
467 status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
469 status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
472 if (status != null) {
473 return status.isSuccess();
480 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
481 if (connectionService == null) {
482 logger.error("Couldn't refer to the ConnectionService");
487 Connection connection = connectionService.getConnection(node);
488 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
490 Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
491 for (String uuid : brTableCache.keySet()) {
492 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
493 if (bridge.getName().contains(bridgeIdentifier)) {
494 return setOFController(node, uuid);
497 } catch(Exception e) {
498 logger.error("Error in setBridgeOFController()",e);
504 public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
505 Connection connection = connectionService.getConnection(node);
506 if (connection == null) return null;
507 OvsdbClient client = connection.getClient();
508 TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
509 if (typedTable == null) return null;
510 return typedTable.getSchema().getName();
514 public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
515 Connection connection = connectionService.getConnection(node);
516 if (connection == null) return null;
517 OvsdbClient client = connection.getClient();
518 return (T)client.getTypedRowWrapper(typedClass, row);
522 public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
523 Connection connection = connectionService.getConnection(node);
524 if (connection == null) return null;
525 OvsdbClient client = connection.getClient();
526 return client.createTypedRowWrapper(typedClass);
529 // SCHEMA-INDEPENDENT Configuration Service APIs
531 private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
532 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
533 if (cache == null) return null;
534 for (String tableName : cache.keySet()) {
535 ConcurrentMap<String, Row> rows = cache.get(tableName);
536 if (rows.get(rowUuid.toString()) != null) {
543 private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
544 Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
545 String refColumn = null;
546 for (String columnName : columnSchemas.keySet()) {
547 ColumnSchema columnSchema = columnSchemas.get(columnName);
548 if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
549 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
550 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
551 if (refColumn == null) {
552 refColumn = columnName;
554 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
559 if (refColumn != null) {
562 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
565 * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
566 * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
568 private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
569 String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
570 Row<GenericTableSchema> row,
571 TransactionBuilder transactionBuilder) {
572 // Insert the row as the first transaction entry
573 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
574 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
575 transactionBuilder.add(op.insert(childTableSchema, row)
578 // Followed by the Mutation
579 if (parentColumn != null) {
580 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
581 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
582 ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
585 .add(op.mutate(parentTableSchema)
586 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
587 .where(_uuid.opEqual(parentUuid))
593 * insert a Row in a Table of a specified Database Schema.
595 * This method can insert just a single Row specified in the row parameter.
596 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
597 * can insert a hierarchy of rows with parent-child relationship.
599 * @param node OVSDB Node
600 * @param databaseName Database Name that represents the Schema supported by the node.
601 * @param tableName Table on which the row is inserted
602 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
603 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
604 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
605 * @param row Row of table Content to be inserted
606 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
607 * @return UUID of the inserted Row
610 public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
611 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
612 Connection connection = connectionService.getConnection(node);
613 OvsdbClient client = connection.getClient();
614 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
615 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
617 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
619 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
620 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
621 return uuid.getData();
625 * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
626 * {@link insertRow(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertRow}
627 * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
628 * the necessary service to populate the Parent Table Name and Parent Column Name.
630 * This method can insert just a single Row specified in the row parameter.
631 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
632 * can insert a hierarchy of rows with parent-child relationship.
634 * @param node OVSDB Node
635 * @param databaseName Database Name that represents the Schema supported by the node.
636 * @param tableName Table on which the row is inserted
637 * @param parentUuid UUID of the parent table to which this operation will result in attaching/mutating.
638 * @param row Row of table Content to be inserted
639 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
640 * @return UUID of the inserted Row
643 public UUID insertRow(Node node, String databaseName, String tableName,
644 UUID parentRowUuid, Row<GenericTableSchema> row)
645 throws OvsdbPluginException {
646 return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
650 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
652 * @param node OVSDB Node
653 * @param databaseName Database Name that represents the Schema supported by the node.
654 * @param tableName Table on which the row is inserted
655 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
656 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
657 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
658 * @param row Row Tree with parent-child relationships via column of type refTable.
659 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
660 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
663 public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
664 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
665 Connection connection = connectionService.getConnection(node);
666 OvsdbClient client = connection.getClient();
668 if (databaseName == null || tableName == null) {
669 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
672 if (parentTable == null && parentUuid != null) {
673 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
676 if (parentColumn == null && parentTable != null) {
677 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
678 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
679 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
682 logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
683 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
685 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
686 extractReferencedRows(node, databaseName, row, referencedRows, 0);
687 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
688 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
690 String namedUuid = "Transaction_"+ tableName;
691 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
692 parentColumn, namedUuid, row, transactionBuilder);
694 int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
695 // Insert Referenced Rows
696 if (referencedRows != null) {
697 for (UUID refUuid : referencedRows.keySet()) {
698 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
699 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
700 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
701 .withId(refUuid.toString()));
705 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
706 List<OperationResult> operationResults;
708 operationResults = results.get();
709 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
710 throw new OvsdbPluginException("Insert Operation Failed");
712 for (OperationResult result : operationResults) {
713 if (result.getError() != null) {
714 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
717 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
718 } catch (InterruptedException | ExecutionException e) {
719 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
724 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
725 * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertTree}
727 * @param node OVSDB Node
728 * @param databaseName Database Name that represents the Schema supported by the node.
729 * @param tableName Table on which the row is inserted
730 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
731 * @param row Row Tree with parent-child relationships via column of type refTable.
732 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
733 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
736 public Row<GenericTableSchema> insertTree(Node node, String databaseName,
737 String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
738 throws OvsdbPluginException {
739 return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
743 * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
744 * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
745 * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
746 * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
747 * entire row and this method will help navigate it through and identify such cases.
748 * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
749 * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
750 * Mutation on the parent rows.
752 * @param node OVSDB Node
753 * @param dbName Database Name that represents the Schema supported by the node.
754 * @param row Row Tree with parent-child relationships via column of type refTable.
755 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
756 * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
757 * This index will help to retain the uniqueness.
759 private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
760 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
761 int namedUuidSuffix) {
762 OvsdbClient client = connectionService.getConnection(node).getClient();
763 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
764 for (Column column : columns) {
765 if (column.getData() != null) {
766 if (column.getData() instanceof ReferencedRow) {
767 ReferencedRow refRowObject = (ReferencedRow)column.getData();
768 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
769 column.setData(refUuid);
771 DatabaseSchema dbSchema = client.getSchema(dbName).get();
772 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
773 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
774 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
775 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
776 } catch (InterruptedException | ExecutionException e) {
777 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
779 } else if (column.getData() instanceof OvsdbSet) {
780 OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
781 OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
782 for (Object obj : setObject) {
783 if (obj instanceof ReferencedRow) {
784 ReferencedRow refRowObject = (ReferencedRow)obj;
785 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
786 modifiedSet.add(refUuid);
788 DatabaseSchema dbSchema = client.getSchema(dbName).get();
789 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
790 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
791 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
792 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
793 } catch (InterruptedException | ExecutionException e) {
794 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
797 modifiedSet.add(obj);
800 column.setData(modifiedSet);
807 * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
808 * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
809 * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
810 * the Row to the application.
812 * @param dbSchema Database Schema supported by the node.
813 * @param row Row Tree with parent-child relationships via column of type refTable.
814 * @param tableName Table on which the row is inserted
815 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
816 * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
817 * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
820 private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
821 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
822 List<OperationResult> operationResults, int referencedRowsInsertIndex) {
823 UUID primaryRowUuid = operationResults.get(0).getUuid();
824 TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
825 ColumnSchema<GenericTableSchema, UUID> _uuid = primaryRowTableSchema.column("_uuid", UUID.class);
827 Column<GenericTableSchema, UUID> _uuidColumn = new Column<GenericTableSchema, UUID>(_uuid, primaryRowUuid);
828 row.addColumn("_uuid", _uuidColumn);
831 if (referencedRows != null) {
832 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
833 if (referencedRows != null) {
834 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
835 UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
836 for (Column column : columns) {
837 if (column.getData() != null) {
838 if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
839 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
840 } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
841 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
842 refSet.remove(refUuid);
843 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
854 public Row<GenericTableSchema> updateRow(Node node, String databaseName,
855 String tableName, UUID rowUuid, Row<GenericTableSchema> row,
856 boolean overwrite) throws OvsdbPluginException {
857 Connection connection = connectionService.getConnection(node);
858 OvsdbClient client = connection.getClient();
860 logger.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
861 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
863 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
864 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
865 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
866 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
867 transactionBuilder.add(op.update(tableSchema, row)
868 .where(_uuid.opEqual(rowUuid))
871 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
872 List<OperationResult> operationResults = results.get();
873 for (OperationResult result : operationResults) {
874 if (result.getError() != null) {
875 throw new OvsdbPluginException("Error updating row : " + result.getError() +
876 " Details: " + result.getDetails());
879 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
880 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
883 return this.getRow(node, databaseName, tableName, rowUuid);
884 } catch(Exception e){
885 throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
890 public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
891 String parentColumn, UUID rowUuid) throws OvsdbPluginException {
892 Connection connection = connectionService.getConnection(node);
893 OvsdbClient client = connection.getClient();
895 if (parentTable == null && parentRowUuid != null) {
896 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
899 if (parentColumn == null && parentTable != null) {
900 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
901 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
902 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
905 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
906 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
908 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
909 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
910 this.processDeleteTransaction(client, databaseName, tableName,
911 parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
913 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
914 List<OperationResult> operationResults;
916 operationResults = results.get();
917 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
918 throw new OvsdbPluginException("Delete Operation Failed");
920 for (OperationResult result : operationResults) {
921 if (result.getError() != null) {
922 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
925 } catch (InterruptedException | ExecutionException e) {
926 // TODO Auto-generated catch block
932 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
933 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
937 public Row<GenericTableSchema> getRow(Node node, String databaseName,
938 String tableName, UUID uuid) throws OvsdbPluginException {
939 ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
941 return rows.get(uuid);
947 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
948 String databaseName, String tableName) throws OvsdbPluginException {
949 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
950 if (ovsTable == null) return null;
951 ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
952 for (String uuidStr : ovsTable.keySet()) {
953 tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
959 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
960 String databaseName, String tableName, String fiqlQuery)
961 throws OvsdbPluginException {
962 return this.getRows(node, databaseName, tableName);
966 public List<String> getTables(Node node, String databaseName) throws OvsdbPluginException {
967 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
968 if (cache == null) return null;
969 return new ArrayList<String>(cache.keySet());