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 LOGGER = LoggerFactory
70 .getLogger(ConfigurationServiceImpl.class);
72 OvsdbConnectionService connectionService;
73 OvsdbInventoryService ovsdbInventoryService;
74 protected static final String OPENFLOW_13 = "1.3";
80 * Function called by the dependency manager when at least one dependency
81 * become unsatisfied or when the component is shutting down because for
82 * example bundle is being stopped.
89 * Function called by dependency manager after "init ()" is called and after
90 * the services provided by the class are registered in the service registry
97 * Function called by the dependency manager before the services exported by
98 * the component are unregistered, this will be followed by a "destroy ()"
105 public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
106 this.connectionService = connectionService;
109 public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
110 if (this.connectionService.equals(connectionService)) {
111 this.connectionService = null;
115 public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
116 this.ovsdbInventoryService = ovsdbInventoryService;
119 public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
120 if (this.ovsdbInventoryService.equals(ovsdbInventoryService)) {
121 this.ovsdbInventoryService = null;
125 private Connection getConnection (Node node) {
126 Connection connection = connectionService.getConnection(node);
127 if (connection == null || !connection.getClient().isActive()) {
134 * There are a few Open_vSwitch schema specific special case handling to be done for
135 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
138 private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
139 String tableName, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
140 Port port = client.getTypedRowWrapper(Port.class, null);
141 if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
142 port = client.getTypedRowWrapper(Port.class, row);
143 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
144 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
145 ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
146 String namedUuid = "Special_"+tableName;
147 List<Operation> priorOperations = transactionBuilder.getOperations();
148 Insert portOperation = (Insert)priorOperations.get(0);
149 portOperation.value(columnSchema, new UUID(namedUuid));
151 Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
152 List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
153 columns.add(nameColumn);
154 Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
155 this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
160 * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
161 * the parent table for the newly inserted Child.
162 * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
164 private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
165 String parentTable, String parentUuid, String parentColumn, String namedUuid,
166 Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
167 this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
168 namedUuid, row, transactionBuilder);
170 * There are a few Open_vSwitch schema specific special case handling to be done for
171 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
174 handleSpecialInsertCase(client, databaseName, childTable, row, transactionBuilder);
178 * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
179 * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
180 * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
183 public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
184 if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
187 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
188 if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
189 Connection connection = connectionService.getConnection(node);
190 OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
191 ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
192 if (row == null || row.size() == 0) {
195 return (String)row.keySet().toArray()[0];
201 * Though this is a New API that takes in Row object, this still is considered a
202 * Deprecated call because of the assumption with a Single Row insertion.
203 * An ideal insertRow must be able to take in multiple Rows, which includes the
204 * Row being inserted in one Table and other Rows that needs mutate in other Tables.
208 public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
209 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
210 if (parentColumn == null) {
211 parentColumn = new String[]{null, null};
214 Connection connection = connectionService.getConnection(node);
215 OvsdbClient client = connection.getClient();
217 String myParentUuid = parentUuid;
218 if (myParentUuid == null) {
219 myParentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
221 LOGGER.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
222 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], myParentUuid, row);
224 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
225 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
227 String namedUuid = "Transaction_"+ tableName;
228 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
229 parentColumn[0], myParentUuid, parentColumn[1], namedUuid,
230 row, transactionBuilder);
232 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
233 List<OperationResult> operationResults;
235 operationResults = results.get();
236 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
237 return new StatusWithUuid(StatusCode.INTERNALERROR);
239 for (OperationResult result : operationResults) {
240 if (result.getError() != null) {
241 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
244 UUID uuid = operationResults.get(0).getUuid();
245 return new StatusWithUuid(StatusCode.SUCCESS, uuid);
246 } catch (InterruptedException | ExecutionException e) {
247 // TODO Auto-generated catch block
248 return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
255 public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
256 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
257 Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
258 return new StatusWithUuid(StatusCode.SUCCESS);
261 private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
262 String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
263 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
264 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
266 if (parentColumn != null) {
267 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
268 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
270 .add(op.mutate(parentTableSchema)
271 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
272 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
276 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
277 transactionBuilder.add(op.delete(childTableSchema)
278 .where(_uuid.opEqual(new UUID(uuid)))
284 public Status deleteRow(Node node, String tableName, String uuid) {
285 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
286 Connection connection = connectionService.getConnection(node);
287 OvsdbClient client = connection.getClient();
289 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
290 if (parentColumn == null) {
291 parentColumn = new String[]{null, null};
294 LOGGER.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
295 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
297 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
298 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
299 this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
300 parentColumn[0], parentColumn[1], uuid, transactionBuilder);
302 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
303 List<OperationResult> operationResults;
305 operationResults = results.get();
306 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
307 return new StatusWithUuid(StatusCode.INTERNALERROR);
309 for (OperationResult result : operationResults) {
310 if (result.getError() != null) {
311 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
314 } catch (InterruptedException | ExecutionException e) {
315 LOGGER.error("Error in deleteRow() {} {}", node, tableName, e);
318 return new Status(StatusCode.SUCCESS);
323 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
324 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
330 public Row getRow(Node node, String tableName, String uuid) {
331 Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
332 if (ovsTable == null) {
335 return ovsTable.get(uuid);
340 public List<String> getTables(Node node) {
341 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
344 private InetAddress getControllerIPAddress(Connection connection) {
345 InetAddress controllerIP = null;
347 String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
349 if (addressString != null) {
351 controllerIP = InetAddress.getByName(addressString);
352 if (controllerIP != null) {
355 } catch (UnknownHostException e) {
356 LOGGER.error("Host {} is invalid", addressString);
360 addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
362 if (addressString != null) {
364 controllerIP = InetAddress.getByName(addressString);
365 if (controllerIP != null) {
368 } catch (UnknownHostException e) {
369 LOGGER.error("Host {} is invalid", addressString);
374 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
376 } catch (Exception e) {
377 LOGGER.debug("Invalid connection provided to getControllerIPAddresses", e);
382 private short getControllerOFPort() {
383 Short defaultOpenFlowPort = 6633;
384 Short openFlowPort = defaultOpenFlowPort;
385 String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
386 if (portString != null) {
388 openFlowPort = Short.decode(portString).shortValue();
389 } catch (NumberFormatException e) {
390 LOGGER.warn("Invalid port:{}, use default({})", portString,
397 private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
398 ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
401 for (Map.Entry<String, Row> entry : rows.entrySet()) {
402 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
403 Column<GenericTableSchema, String> column = currController.getTargetColumn();
404 String currTarget = column.getData();
405 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
406 return currController.getUuid();
414 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
415 Connection connection = this.getConnection(node);
416 if (connection == null) {
420 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
422 Status updateOperationStatus = null;
424 OvsdbSet<String> protocols = new OvsdbSet<String>();
426 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
431 protocols.add("OpenFlow13");
434 bridge.setProtocols(protocols);
435 updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
436 null, bridgeUUID, bridge.getRow());
437 LOGGER.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
438 protocols.toArray()[0], updateOperationStatus);
440 } catch (SchemaVersionMismatchException e){
441 LOGGER.debug(e.toString());
444 // If we fail to update the protocols
445 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
446 return updateOperationStatus.isSuccess();
449 Status status = null;
450 UUID currControllerUuid = null;
451 InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
452 short ofControllerPort = getControllerOFPort();
453 String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
454 Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
455 newController.setTarget(newControllerTarget);
456 final String controllerTableName = newController.getSchema().getName();
458 currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
460 if (currControllerUuid != null) {
461 bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
462 bridge.setController(Sets.newHashSet(currControllerUuid));
463 status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
465 status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
468 if (status != null) {
469 return status.isSuccess();
476 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
477 if (connectionService == null) {
478 LOGGER.error("Couldn't refer to the ConnectionService");
483 Connection connection = connectionService.getConnection(node);
484 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
486 Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
487 for (String uuid : brTableCache.keySet()) {
488 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
489 if (bridge.getName().contains(bridgeIdentifier)) {
490 return setOFController(node, uuid);
493 } catch(Exception e) {
494 LOGGER.error("Error in setBridgeOFController()", e);
500 public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
501 Connection connection = connectionService.getConnection(node);
502 if (connection == null) return null;
503 OvsdbClient client = connection.getClient();
504 TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
505 if (typedTable == null) return null;
506 return typedTable.getSchema().getName();
510 public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
511 Connection connection = connectionService.getConnection(node);
512 if (connection == null) return null;
513 OvsdbClient client = connection.getClient();
514 return (T)client.getTypedRowWrapper(typedClass, row);
518 public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
519 Connection connection = connectionService.getConnection(node);
520 if (connection == null) return null;
521 OvsdbClient client = connection.getClient();
522 return client.createTypedRowWrapper(typedClass);
525 // SCHEMA-INDEPENDENT Configuration Service APIs
527 private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
528 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
529 if (cache == null) return null;
530 for (String tableName : cache.keySet()) {
531 ConcurrentMap<String, Row> rows = cache.get(tableName);
532 if (rows.get(rowUuid.toString()) != null) {
539 private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
540 Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
541 String refColumn = null;
542 for (String columnName : columnSchemas.keySet()) {
543 ColumnSchema columnSchema = columnSchemas.get(columnName);
544 if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
545 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
546 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
547 if (refColumn == null) {
548 refColumn = columnName;
550 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
555 if (refColumn != null) {
558 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
561 * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
562 * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
564 private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
565 String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
566 Row<GenericTableSchema> row,
567 TransactionBuilder transactionBuilder) {
568 // Insert the row as the first transaction entry
569 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
570 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
571 transactionBuilder.add(op.insert(childTableSchema, row)
574 // Followed by the Mutation
575 if (parentColumn != null) {
576 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
577 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
578 ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
581 .add(op.mutate(parentTableSchema)
582 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
583 .where(_uuid.opEqual(parentUuid))
589 * insert a Row in a Table of a specified Database Schema.
591 * This method can insert just a single Row specified in the row parameter.
592 * But {@link #insertTree(Node, String, String, UUID, Row) insertTree}
593 * can insert a hierarchy of rows with parent-child relationship.
595 * @param node OVSDB Node
596 * @param databaseName Database Name that represents the Schema supported by the node.
597 * @param tableName Table on which the row is inserted
598 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
599 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
600 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
601 * @param row Row of table Content to be inserted
602 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
603 * @return UUID of the inserted Row
606 public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
607 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
608 Connection connection = connectionService.getConnection(node);
609 OvsdbClient client = connection.getClient();
610 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
611 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
613 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
615 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
616 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
617 return uuid.getData();
621 * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
622 * {@link insertRow(Node, String, String, String, UUID, String, Row) insertRow}
623 * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
624 * the necessary service to populate the Parent Table Name and Parent Column Name.
626 * This method can insert just a single Row specified in the row parameter.
627 * But {@link #insertTree(Node, String, String, UUID, Row) insertTree}
628 * can insert a hierarchy of rows with parent-child relationship.
630 * @param node OVSDB Node
631 * @param databaseName Database Name that represents the Schema supported by the node.
632 * @param tableName Table on which the row is inserted
633 * @param parentRowUuid UUID of the parent table to which this operation will result in attaching/mutating.
634 * @param row Row of table Content to be inserted
635 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
636 * @return UUID of the inserted Row
639 public UUID insertRow(Node node, String databaseName, String tableName,
640 UUID parentRowUuid, Row<GenericTableSchema> row)
641 throws OvsdbPluginException {
642 return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
646 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
648 * @param node OVSDB Node
649 * @param databaseName Database Name that represents the Schema supported by the node.
650 * @param tableName Table on which the row is inserted
651 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
652 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
653 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
654 * @param row Row Tree with parent-child relationships via column of type refTable.
655 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
656 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
659 public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
660 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
661 Connection connection = connectionService.getConnection(node);
662 OvsdbClient client = connection.getClient();
664 if (databaseName == null || tableName == null) {
665 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
668 if (parentTable == null && parentUuid != null) {
669 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
672 if (parentColumn == null && parentTable != null) {
673 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
674 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
675 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
678 LOGGER.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
679 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
681 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
682 extractReferencedRows(node, databaseName, row, referencedRows, 0);
683 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
684 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
686 String namedUuid = "Transaction_"+ tableName;
687 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
688 parentColumn, namedUuid, row, transactionBuilder);
690 int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
691 // Insert Referenced Rows
692 if (referencedRows != null) {
693 for (UUID refUuid : referencedRows.keySet()) {
694 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
695 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
696 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
697 .withId(refUuid.toString()));
701 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
702 List<OperationResult> operationResults;
704 operationResults = results.get();
705 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
706 throw new OvsdbPluginException("Insert Operation Failed");
708 for (OperationResult result : operationResults) {
709 if (result.getError() != null) {
710 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
713 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
714 } catch (InterruptedException | ExecutionException e) {
715 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
720 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
721 * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row) insertTree}
723 * @param node OVSDB Node
724 * @param databaseName Database Name that represents the Schema supported by the node.
725 * @param tableName Table on which the row is inserted
726 * @param parentRowUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
727 * @param row Row Tree with parent-child relationships via column of type refTable.
728 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
729 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
732 public Row<GenericTableSchema> insertTree(Node node, String databaseName,
733 String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
734 throws OvsdbPluginException {
735 return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
739 * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
740 * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
741 * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
742 * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
743 * entire row and this method will help navigate it through and identify such cases.
744 * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
745 * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
746 * Mutation on the parent rows.
748 * @param node OVSDB Node
749 * @param dbName Database Name that represents the Schema supported by the node.
750 * @param row Row Tree with parent-child relationships via column of type refTable.
751 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
752 * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
753 * This index will help to retain the uniqueness.
755 private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
756 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
757 int namedUuidSuffix) {
758 OvsdbClient client = connectionService.getConnection(node).getClient();
759 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
760 for (Column column : columns) {
761 if (column.getData() != null) {
762 if (column.getData() instanceof ReferencedRow) {
763 ReferencedRow refRowObject = (ReferencedRow)column.getData();
764 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
765 column.setData(refUuid);
767 DatabaseSchema dbSchema = client.getSchema(dbName).get();
768 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
769 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
770 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
771 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
772 } catch (InterruptedException | ExecutionException e) {
773 LOGGER.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
775 } else if (column.getData() instanceof OvsdbSet) {
776 OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
777 OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
778 for (Object obj : setObject) {
779 if (obj instanceof ReferencedRow) {
780 ReferencedRow refRowObject = (ReferencedRow)obj;
781 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
782 modifiedSet.add(refUuid);
784 DatabaseSchema dbSchema = client.getSchema(dbName).get();
785 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
786 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
787 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
788 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
789 } catch (InterruptedException | ExecutionException e) {
790 LOGGER.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
793 modifiedSet.add(obj);
796 column.setData(modifiedSet);
803 * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
804 * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
805 * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
806 * the Row to the application.
808 * @param dbSchema Database Schema supported by the node.
809 * @param row Row Tree with parent-child relationships via column of type refTable.
810 * @param tableName Table on which the row is inserted
811 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
812 * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
813 * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
816 private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
817 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
818 List<OperationResult> operationResults, int referencedRowsInsertIndex) {
819 UUID primaryRowUuid = operationResults.get(0).getUuid();
820 TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
821 ColumnSchema<GenericTableSchema, UUID> uuid = primaryRowTableSchema.column("_uuid", UUID.class);
823 Column<GenericTableSchema, UUID> uuidColumn = new Column<GenericTableSchema, UUID>(uuid, primaryRowUuid);
824 row.addColumn("_uuid", uuidColumn);
827 if (referencedRows != null) {
828 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
829 if (referencedRows != null) {
830 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
831 UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
832 for (Column column : columns) {
833 if (column.getData() != null) {
834 if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
835 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
836 } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
837 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
838 refSet.remove(refUuid);
839 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
850 public Row<GenericTableSchema> updateRow(Node node, String databaseName,
851 String tableName, UUID rowUuid, Row<GenericTableSchema> row,
853 Connection connection = connectionService.getConnection(node);
854 OvsdbClient client = connection.getClient();
856 LOGGER.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
857 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
859 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
860 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
861 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
862 ColumnSchema<GenericTableSchema, UUID> uuid = tableSchema.column("_uuid", UUID.class);
863 transactionBuilder.add(op.update(tableSchema, row)
864 .where(uuid.opEqual(rowUuid))
867 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
868 List<OperationResult> operationResults = results.get();
869 for (OperationResult result : operationResults) {
870 if (result.getError() != null) {
871 throw new OvsdbPluginException("Error updating row : " + result.getError() +
872 " Details: " + result.getDetails());
875 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
876 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
879 return this.getRow(node, databaseName, tableName, rowUuid);
880 } catch(Exception e){
881 throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
886 public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
887 String parentColumn, UUID rowUuid) {
888 Connection connection = connectionService.getConnection(node);
889 OvsdbClient client = connection.getClient();
891 if (parentTable == null && parentRowUuid != null) {
892 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
895 String myParentColumn = parentColumn;
896 if (myParentColumn == null && parentTable != null) {
897 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
898 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
899 myParentColumn = this.getReferencingColumn(parentTableSchema, tableName);
902 LOGGER.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
903 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, myParentColumn);
905 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
906 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
907 this.processDeleteTransaction(client, databaseName, tableName,
908 parentTable, myParentColumn, rowUuid.toString(), transactionBuilder);
910 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
911 List<OperationResult> operationResults;
913 operationResults = results.get();
914 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
915 throw new OvsdbPluginException("Delete Operation Failed");
917 for (OperationResult result : operationResults) {
918 if (result.getError() != null) {
919 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
922 } catch (InterruptedException | ExecutionException e) {
923 LOGGER.error("Error in deleteRow() {} {} {} {}", node, databaseName, tableName, parentTable, e);
928 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) {
929 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
933 public Row<GenericTableSchema> getRow(Node node, String databaseName,
934 String tableName, UUID uuid) {
935 ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
937 return rows.get(uuid);
943 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
944 String databaseName, String tableName) throws OvsdbPluginException {
945 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
946 if (ovsTable == null) {
949 ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
950 for (String uuidStr : ovsTable.keySet()) {
951 tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
957 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
958 String databaseName, String tableName, String fiqlQuery) {
959 return this.getRows(node, databaseName, tableName);
963 public List<String> getTables(Node node, String databaseName) {
964 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
968 return new ArrayList<String>(cache.keySet());