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.opendaylight.ovsdb.plugin.api.Status;
26 import org.opendaylight.ovsdb.plugin.api.StatusCode;
27 import org.opendaylight.ovsdb.lib.OvsdbClient;
28 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
29 import org.opendaylight.ovsdb.lib.notation.Column;
30 import org.opendaylight.ovsdb.lib.notation.Mutator;
31 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
32 import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
33 import org.opendaylight.ovsdb.lib.notation.Row;
34 import org.opendaylight.ovsdb.lib.notation.UUID;
35 import org.opendaylight.ovsdb.lib.operations.Insert;
36 import org.opendaylight.ovsdb.lib.operations.Operation;
37 import org.opendaylight.ovsdb.lib.operations.OperationResult;
38 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
39 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
40 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
41 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
42 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
43 import org.opendaylight.ovsdb.lib.schema.TableSchema;
44 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
45 import org.opendaylight.ovsdb.plugin.api.Connection;
46 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
47 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
48 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
49 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryService;
50 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
51 import org.opendaylight.ovsdb.plugin.error.OvsdbPluginException;
52 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
53 import org.opendaylight.ovsdb.schema.openvswitch.Controller;
54 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
55 import org.opendaylight.ovsdb.schema.openvswitch.Port;
56 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
62 import com.fasterxml.jackson.databind.node.ObjectNode;
63 import com.google.common.collect.Maps;
64 import com.google.common.collect.Sets;
65 import com.google.common.util.concurrent.ListenableFuture;
67 public class ConfigurationServiceImpl implements OvsdbConfigurationService
69 private static final Logger LOG = LoggerFactory.getLogger(ConfigurationServiceImpl.class);
71 OvsdbConnectionService connectionService;
72 OvsdbInventoryService ovsdbInventoryService;
73 protected static final String OPENFLOW_13 = "1.3";
79 * Function called by the dependency manager when at least one dependency
80 * become unsatisfied or when the component is shutting down because for
81 * example bundle is being stopped.
88 * Function called by dependency manager after "init ()" is called and after
89 * the services provided by the class are registered in the service registry
96 * Function called by the dependency manager before the services exported by
97 * the component are unregistered, this will be followed by a "destroy ()"
104 public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
105 this.connectionService = connectionService;
108 public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
109 if (this.connectionService.equals(connectionService)) {
110 this.connectionService = null;
114 public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
115 this.ovsdbInventoryService = ovsdbInventoryService;
118 public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
119 if (this.ovsdbInventoryService.equals(ovsdbInventoryService)) {
120 this.ovsdbInventoryService = null;
124 private Connection getConnection (Node node) {
125 Connection connection = connectionService.getConnection(node);
126 if (connection == null || !connection.getClient().isActive()) {
133 * There are a few Open_vSwitch schema specific special case handling to be done for
134 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
137 private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
138 String tableName, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
139 Port port = client.getTypedRowWrapper(Port.class, null);
140 if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
141 port = client.getTypedRowWrapper(Port.class, row);
142 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
143 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
144 ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
145 String namedUuid = "Special_"+tableName;
146 List<Operation> priorOperations = transactionBuilder.getOperations();
147 Insert portOperation = (Insert)priorOperations.get(0);
148 portOperation.value(columnSchema, new UUID(namedUuid));
150 Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
151 List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
152 columns.add(nameColumn);
153 Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
154 this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
159 * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
160 * the parent table for the newly inserted Child.
161 * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
163 private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
164 String parentTable, String parentUuid, String parentColumn, String namedUuid,
165 Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
166 this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
167 namedUuid, row, transactionBuilder);
169 * There are a few Open_vSwitch schema specific special case handling to be done for
170 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
173 handleSpecialInsertCase(client, databaseName, childTable, row, transactionBuilder);
177 * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
178 * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
179 * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
182 public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
183 if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
186 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
187 if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
188 Connection connection = connectionService.getConnection(node);
189 OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
190 ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
191 if (row == null || row.size() == 0) {
194 return (String)row.keySet().toArray()[0];
200 * Though this is a New API that takes in Row object, this still is considered a
201 * Deprecated call because of the assumption with a Single Row insertion.
202 * An ideal insertRow must be able to take in multiple Rows, which includes the
203 * Row being inserted in one Table and other Rows that needs mutate in other Tables.
207 public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
208 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
209 if (parentColumn == null) {
210 parentColumn = new String[]{null, null};
213 Connection connection = connectionService.getConnection(node);
214 OvsdbClient client = connection.getClient();
216 String myParentUuid = parentUuid;
217 if (myParentUuid == null) {
218 myParentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
220 LOG.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
221 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], myParentUuid, row);
223 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
224 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
226 String namedUuid = "Transaction_"+ tableName;
227 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
228 parentColumn[0], myParentUuid, parentColumn[1], namedUuid,
229 row, transactionBuilder);
231 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
232 List<OperationResult> operationResults;
234 operationResults = results.get();
235 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
236 return new StatusWithUuid(StatusCode.INTERNALERROR);
238 for (OperationResult result : operationResults) {
239 if (result.getError() != null) {
240 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
243 UUID uuid = operationResults.get(0).getUuid();
244 return new StatusWithUuid(StatusCode.SUCCESS, uuid);
245 } catch (InterruptedException | ExecutionException e) {
246 // TODO Auto-generated catch block
247 return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
254 public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
255 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
256 Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
257 return new StatusWithUuid(StatusCode.SUCCESS);
260 private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
261 String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
262 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
263 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
265 if (parentColumn != null) {
266 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
267 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
269 .add(op.mutate(parentTableSchema)
270 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
271 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
275 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
276 transactionBuilder.add(op.delete(childTableSchema)
277 .where(_uuid.opEqual(new UUID(uuid)))
283 public Status deleteRow(Node node, String tableName, String uuid) {
284 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
285 Connection connection = connectionService.getConnection(node);
286 OvsdbClient client = connection.getClient();
288 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
289 if (parentColumn == null) {
290 parentColumn = new String[]{null, null};
293 LOG.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
294 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
296 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
297 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
298 this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
299 parentColumn[0], parentColumn[1], uuid, transactionBuilder);
301 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
302 List<OperationResult> operationResults;
304 operationResults = results.get();
305 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
306 return new StatusWithUuid(StatusCode.INTERNALERROR);
308 for (OperationResult result : operationResults) {
309 if (result.getError() != null) {
310 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
313 } catch (InterruptedException | ExecutionException e) {
314 LOG.error("Error in deleteRow() {} {}", node, tableName, e);
317 return new Status(StatusCode.SUCCESS);
322 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
323 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
329 public Row getRow(Node node, String tableName, String uuid) {
330 Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
331 if (ovsTable == null) {
334 return ovsTable.get(uuid);
339 public List<String> getTables(Node node) {
340 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
343 private InetAddress getControllerIPAddress(Connection connection) {
344 InetAddress controllerIP = null;
346 String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
348 if (addressString != null) {
350 controllerIP = InetAddress.getByName(addressString);
351 if (controllerIP != null) {
354 } catch (UnknownHostException e) {
355 LOG.error("Host {} is invalid", addressString);
359 addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
361 if (addressString != null) {
363 controllerIP = InetAddress.getByName(addressString);
364 if (controllerIP != null) {
367 } catch (UnknownHostException e) {
368 LOG.error("Host {} is invalid", addressString);
373 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
375 } catch (Exception e) {
376 LOG.debug("Invalid connection provided to getControllerIPAddresses", e);
381 private short getControllerOFPort() {
382 Short defaultOpenFlowPort = 6633;
383 Short openFlowPort = defaultOpenFlowPort;
384 String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
385 if (portString != null) {
387 openFlowPort = Short.decode(portString).shortValue();
388 } catch (NumberFormatException e) {
389 LOG.warn("Invalid port:{}, use default({})", portString,
396 private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
397 ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
400 for (Map.Entry<String, Row> entry : rows.entrySet()) {
401 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
402 Column<GenericTableSchema, String> column = currController.getTargetColumn();
403 String currTarget = column.getData();
404 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
405 return currController.getUuid();
413 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
414 Connection connection = this.getConnection(node);
415 if (connection == null) {
419 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
421 Status updateOperationStatus = null;
423 OvsdbSet<String> protocols = new OvsdbSet<String>();
425 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
430 protocols.add("OpenFlow13");
433 bridge.setProtocols(protocols);
434 updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
435 null, bridgeUUID, bridge.getRow());
436 LOG.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
437 protocols.toArray()[0], updateOperationStatus);
439 } catch (SchemaVersionMismatchException e){
440 LOG.debug(e.toString());
443 // If we fail to update the protocols
444 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
445 return updateOperationStatus.isSuccess();
448 Status status = null;
449 UUID currControllerUuid = null;
450 InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
451 short ofControllerPort = getControllerOFPort();
452 String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
453 Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
454 newController.setTarget(newControllerTarget);
455 final String controllerTableName = newController.getSchema().getName();
457 currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
459 if (currControllerUuid != null) {
460 bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
461 bridge.setController(Sets.newHashSet(currControllerUuid));
462 status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
464 status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
467 if (status != null) {
468 return status.isSuccess();
475 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
476 if (connectionService == null) {
477 LOG.error("Couldn't refer to the ConnectionService");
482 Connection connection = connectionService.getConnection(node);
483 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
485 Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
486 for (String uuid : brTableCache.keySet()) {
487 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
488 if (bridge.getName().contains(bridgeIdentifier)) {
489 return setOFController(node, uuid);
492 } catch(Exception e) {
493 LOG.error("Error in setBridgeOFController()", e);
499 public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
500 Connection connection = connectionService.getConnection(node);
501 if (connection == null) {
504 OvsdbClient client = connection.getClient();
505 TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
506 if (typedTable == null) {
509 return typedTable.getSchema().getName();
513 public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
514 Connection connection = connectionService.getConnection(node);
515 if (connection == null) {
518 OvsdbClient client = connection.getClient();
519 return (T)client.getTypedRowWrapper(typedClass, row);
523 public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
524 Connection connection = connectionService.getConnection(node);
525 if (connection == null) {
528 OvsdbClient client = connection.getClient();
529 return client.createTypedRowWrapper(typedClass);
532 // SCHEMA-INDEPENDENT Configuration Service APIs
534 private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
535 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
539 for (String tableName : cache.keySet()) {
540 ConcurrentMap<String, Row> rows = cache.get(tableName);
541 if (rows.get(rowUuid.toString()) != null) {
548 private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
549 Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
550 String refColumn = null;
551 for (String columnName : columnSchemas.keySet()) {
552 ColumnSchema columnSchema = columnSchemas.get(columnName);
553 if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
554 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
555 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
556 if (refColumn == null) {
557 refColumn = columnName;
559 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
564 if (refColumn != null) {
567 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
570 * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
571 * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
573 private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
574 String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
575 Row<GenericTableSchema> row,
576 TransactionBuilder transactionBuilder) {
577 // Insert the row as the first transaction entry
578 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
579 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
580 transactionBuilder.add(op.insert(childTableSchema, row)
583 // Followed by the Mutation
584 if (parentColumn != null) {
585 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
586 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
587 ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
590 .add(op.mutate(parentTableSchema)
591 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
592 .where(_uuid.opEqual(parentUuid))
598 * insert a Row in a Table of a specified Database Schema.
600 * This method can insert just a single Row specified in the row parameter.
601 * But {@link #insertTree(Node, String, String, UUID, Row) insertTree}
602 * can insert a hierarchy of rows with parent-child relationship.
604 * @param node OVSDB Node
605 * @param databaseName Database Name that represents the Schema supported by the node.
606 * @param tableName Table on which the row is inserted
607 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
608 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
609 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
610 * @param row Row of table Content to be inserted
611 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
612 * @return UUID of the inserted Row
615 public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
616 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
617 Connection connection = connectionService.getConnection(node);
618 OvsdbClient client = connection.getClient();
619 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
620 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
622 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
624 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
625 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
626 return uuid.getData();
630 * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
631 * {@link insertRow(Node, String, String, String, UUID, String, Row) insertRow}
632 * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
633 * the necessary service to populate the Parent Table Name and Parent Column Name.
635 * This method can insert just a single Row specified in the row parameter.
636 * But {@link #insertTree(Node, String, String, UUID, Row) insertTree}
637 * can insert a hierarchy of rows with parent-child relationship.
639 * @param node OVSDB Node
640 * @param databaseName Database Name that represents the Schema supported by the node.
641 * @param tableName Table on which the row is inserted
642 * @param parentRowUuid UUID of the parent table to which this operation will result in attaching/mutating.
643 * @param row Row of table Content to be inserted
644 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
645 * @return UUID of the inserted Row
648 public UUID insertRow(Node node, String databaseName, String tableName,
649 UUID parentRowUuid, Row<GenericTableSchema> row)
650 throws OvsdbPluginException {
651 return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
655 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
657 * @param node OVSDB Node
658 * @param databaseName Database Name that represents the Schema supported by the node.
659 * @param tableName Table on which the row is inserted
660 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
661 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
662 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
663 * @param row Row Tree with parent-child relationships via column of type refTable.
664 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
665 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
668 public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
669 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
670 Connection connection = connectionService.getConnection(node);
671 OvsdbClient client = connection.getClient();
673 if (databaseName == null || tableName == null) {
674 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
677 if (parentTable == null && parentUuid != null) {
678 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
681 if (parentColumn == null && parentTable != null) {
682 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
683 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
684 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
687 LOG.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
688 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
690 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
691 extractReferencedRows(node, databaseName, row, referencedRows, 0);
692 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
693 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
695 String namedUuid = "Transaction_"+ tableName;
696 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
697 parentColumn, namedUuid, row, transactionBuilder);
699 int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
700 // Insert Referenced Rows
701 if (referencedRows != null) {
702 for (UUID refUuid : referencedRows.keySet()) {
703 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
704 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
705 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
706 .withId(refUuid.toString()));
710 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
711 List<OperationResult> operationResults;
713 operationResults = results.get();
714 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
715 throw new OvsdbPluginException("Insert Operation Failed");
717 for (OperationResult result : operationResults) {
718 if (result.getError() != null) {
719 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
722 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
723 } catch (InterruptedException | ExecutionException e) {
724 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
729 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
730 * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row) insertTree}
732 * @param node OVSDB Node
733 * @param databaseName Database Name that represents the Schema supported by the node.
734 * @param tableName Table on which the row is inserted
735 * @param parentRowUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
736 * @param row Row Tree with parent-child relationships via column of type refTable.
737 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
738 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
741 public Row<GenericTableSchema> insertTree(Node node, String databaseName,
742 String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
743 throws OvsdbPluginException {
744 return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
748 * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
749 * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
750 * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
751 * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
752 * entire row and this method will help navigate it through and identify such cases.
753 * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
754 * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
755 * Mutation on the parent rows.
757 * @param node OVSDB Node
758 * @param dbName Database Name that represents the Schema supported by the node.
759 * @param row Row Tree with parent-child relationships via column of type refTable.
760 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
761 * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
762 * This index will help to retain the uniqueness.
764 private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
765 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
766 int namedUuidSuffix) {
767 OvsdbClient client = connectionService.getConnection(node).getClient();
768 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
769 for (Column column : columns) {
770 if (column.getData() != null) {
771 if (column.getData() instanceof ReferencedRow) {
772 ReferencedRow refRowObject = (ReferencedRow)column.getData();
773 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
774 column.setData(refUuid);
776 DatabaseSchema dbSchema = client.getSchema(dbName).get();
777 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
778 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
779 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
780 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
781 } catch (InterruptedException | ExecutionException e) {
782 LOG.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
784 } else if (column.getData() instanceof OvsdbSet) {
785 OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
786 OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
787 for (Object obj : setObject) {
788 if (obj instanceof ReferencedRow) {
789 ReferencedRow refRowObject = (ReferencedRow)obj;
790 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
791 modifiedSet.add(refUuid);
793 DatabaseSchema dbSchema = client.getSchema(dbName).get();
794 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
795 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
796 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
797 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
798 } catch (InterruptedException | ExecutionException e) {
799 LOG.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
802 modifiedSet.add(obj);
805 column.setData(modifiedSet);
812 * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
813 * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
814 * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
815 * the Row to the application.
817 * @param dbSchema Database Schema supported by the node.
818 * @param row Row Tree with parent-child relationships via column of type refTable.
819 * @param tableName Table on which the row is inserted
820 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
821 * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
822 * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
825 private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
826 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
827 List<OperationResult> operationResults, int referencedRowsInsertIndex) {
828 UUID primaryRowUuid = operationResults.get(0).getUuid();
829 TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
830 ColumnSchema<GenericTableSchema, UUID> uuid = primaryRowTableSchema.column("_uuid", UUID.class);
832 Column<GenericTableSchema, UUID> uuidColumn = new Column<GenericTableSchema, UUID>(uuid, primaryRowUuid);
833 row.addColumn("_uuid", uuidColumn);
836 if (referencedRows != null) {
837 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
838 if (referencedRows != null) {
839 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
840 UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
841 for (Column column : columns) {
842 if (column.getData() != null) {
843 if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
844 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
845 } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
846 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
847 refSet.remove(refUuid);
848 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
859 public Row<GenericTableSchema> updateRow(Node node, String databaseName,
860 String tableName, UUID rowUuid, Row<GenericTableSchema> row,
862 Connection connection = connectionService.getConnection(node);
863 OvsdbClient client = connection.getClient();
865 LOG.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
866 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
868 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
869 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
870 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
871 ColumnSchema<GenericTableSchema, UUID> uuid = tableSchema.column("_uuid", UUID.class);
872 transactionBuilder.add(op.update(tableSchema, row)
873 .where(uuid.opEqual(rowUuid))
876 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
877 List<OperationResult> operationResults = results.get();
878 for (OperationResult result : operationResults) {
879 if (result.getError() != null) {
880 throw new OvsdbPluginException("Error updating row : " + result.getError() +
881 " Details: " + result.getDetails());
884 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
885 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
888 return this.getRow(node, databaseName, tableName, rowUuid);
889 } catch(Exception e){
890 throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
895 public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
896 String parentColumn, UUID rowUuid) {
897 Connection connection = connectionService.getConnection(node);
898 OvsdbClient client = connection.getClient();
900 if (parentTable == null && parentRowUuid != null) {
901 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
904 String myParentColumn = parentColumn;
905 if (myParentColumn == null && parentTable != null) {
906 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
907 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
908 myParentColumn = this.getReferencingColumn(parentTableSchema, tableName);
911 LOG.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
912 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, myParentColumn);
914 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
915 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
916 this.processDeleteTransaction(client, databaseName, tableName,
917 parentTable, myParentColumn, rowUuid.toString(), transactionBuilder);
919 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
920 List<OperationResult> operationResults;
922 operationResults = results.get();
923 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
924 throw new OvsdbPluginException("Delete Operation Failed");
926 for (OperationResult result : operationResults) {
927 if (result.getError() != null) {
928 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
931 } catch (InterruptedException | ExecutionException e) {
932 LOG.error("Error in deleteRow() {} {} {} {}", node, databaseName, tableName, parentTable, e);
937 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) {
938 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
942 public Row<GenericTableSchema> getRow(Node node, String databaseName,
943 String tableName, UUID uuid) {
944 ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
946 return rows.get(uuid);
952 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
953 String databaseName, String tableName) throws OvsdbPluginException {
954 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
955 if (ovsTable == null) {
958 ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
959 for (String uuidStr : ovsTable.keySet()) {
960 tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
966 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
967 String databaseName, String tableName, String fiqlQuery) {
968 return this.getRows(node, databaseName, tableName);
972 public List<String> getTables(Node node, String databaseName) {
973 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
977 return new ArrayList<String>(cache.keySet());