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.controller.sal.core.Node;
26 import org.opendaylight.ovsdb.lib.OvsdbClient;
27 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
28 import org.opendaylight.ovsdb.lib.notation.Column;
29 import org.opendaylight.ovsdb.lib.notation.Mutator;
30 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
31 import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
32 import org.opendaylight.ovsdb.lib.notation.Row;
33 import org.opendaylight.ovsdb.lib.notation.UUID;
34 import org.opendaylight.ovsdb.lib.operations.Insert;
35 import org.opendaylight.ovsdb.lib.operations.Operation;
36 import org.opendaylight.ovsdb.lib.operations.OperationResult;
37 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
38 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
39 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
40 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
41 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
42 import org.opendaylight.ovsdb.lib.schema.TableSchema;
43 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
44 import org.opendaylight.ovsdb.plugin.api.Connection;
45 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
46 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
47 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
48 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryService;
49 import org.opendaylight.ovsdb.plugin.api.Status;
50 import org.opendaylight.ovsdb.plugin.api.StatusCode;
51 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
52 import org.opendaylight.ovsdb.plugin.error.OvsdbPluginException;
53 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
54 import org.opendaylight.ovsdb.schema.openvswitch.Controller;
55 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
56 import org.opendaylight.ovsdb.schema.openvswitch.Port;
57 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
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 == 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 == 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, String uuid, 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, namedUuid, 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)) return null;
185 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
186 if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
187 Connection connection = connectionService.getConnection(node);
188 OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
189 ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
190 if (row == null || row.size() == 0) return null;
191 return (String)row.keySet().toArray()[0];
197 * Though this is a New API that takes in Row object, this still is considered a
198 * Deprecated call because of the assumption with a Single Row insertion.
199 * An ideal insertRow must be able to take in multiple Rows, which includes the
200 * Row being inserted in one Table and other Rows that needs mutate in other Tables.
204 public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
205 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
206 if (parentColumn == null) {
207 parentColumn = new String[]{null, null};
210 Connection connection = connectionService.getConnection(node);
211 OvsdbClient client = connection.getClient();
213 if (parentUuid == null) {
214 parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
216 logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
217 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
219 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
220 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
222 String namedUuid = "Transaction_"+ tableName;
223 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
224 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
225 row, transactionBuilder);
227 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
228 List<OperationResult> operationResults;
230 operationResults = results.get();
231 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
232 return new StatusWithUuid(StatusCode.INTERNALERROR);
234 for (OperationResult result : operationResults) {
235 if (result.getError() != null) {
236 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
239 UUID uuid = operationResults.get(0).getUuid();
240 return new StatusWithUuid(StatusCode.SUCCESS, uuid);
241 } catch (InterruptedException | ExecutionException e) {
242 // TODO Auto-generated catch block
243 return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
250 public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
251 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
252 Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
253 return new StatusWithUuid(StatusCode.SUCCESS);
256 private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
257 String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
258 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
259 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
261 if (parentColumn != null) {
262 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
263 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
265 .add(op.mutate(parentTableSchema)
266 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
267 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
271 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
272 transactionBuilder.add(op.delete(childTableSchema)
273 .where(_uuid.opEqual(new UUID(uuid)))
279 public Status deleteRow(Node node, String tableName, String uuid) {
280 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
281 Connection connection = connectionService.getConnection(node);
282 OvsdbClient client = connection.getClient();
284 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
285 if (parentColumn == null) {
286 parentColumn = new String[]{null, null};
289 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
290 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
292 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
293 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
294 this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
295 parentColumn[0], parentColumn[1], uuid, transactionBuilder);
297 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
298 List<OperationResult> operationResults;
300 operationResults = results.get();
301 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
302 return new StatusWithUuid(StatusCode.INTERNALERROR);
304 for (OperationResult result : operationResults) {
305 if (result.getError() != null) {
306 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
309 } catch (InterruptedException | ExecutionException e) {
310 logger.error("Error in deleteRow() {} {}", node, tableName, e);
313 return new Status(StatusCode.SUCCESS);
318 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
319 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
325 public Row getRow(Node node, String tableName, String uuid) {
326 Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
327 if (ovsTable == null) return null;
328 return ovsTable.get(uuid);
333 public List<String> getTables(Node node) {
334 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
337 private InetAddress getControllerIPAddress(Connection connection) {
338 InetAddress controllerIP = null;
340 String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
342 if (addressString != null) {
344 controllerIP = InetAddress.getByName(addressString);
345 if (controllerIP != null) {
348 } catch (UnknownHostException e) {
349 logger.error("Host {} is invalid", addressString);
353 addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
355 if (addressString != null) {
357 controllerIP = InetAddress.getByName(addressString);
358 if (controllerIP != null) {
361 } catch (UnknownHostException e) {
362 logger.error("Host {} is invalid", addressString);
367 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
369 } catch (Exception e) {
370 logger.debug("Invalid connection provided to getControllerIPAddresses", e);
375 private short getControllerOFPort() {
376 Short defaultOpenFlowPort = 6633;
377 Short openFlowPort = defaultOpenFlowPort;
378 String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
379 if (portString != null) {
381 openFlowPort = Short.decode(portString).shortValue();
382 } catch (NumberFormatException e) {
383 logger.warn("Invalid port:{}, use default({})", portString,
390 private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
391 ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
394 for (Map.Entry<String, Row> entry : rows.entrySet()) {
395 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
396 Column<GenericTableSchema, String> column = currController.getTargetColumn();
397 String currTarget = column.getData();
398 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
399 return currController.getUuid();
407 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
408 Connection connection = this.getConnection(node);
409 if (connection == null) {
413 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
415 Status updateOperationStatus = null;
417 OvsdbSet<String> protocols = new OvsdbSet<String>();
419 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
424 protocols.add("OpenFlow13");
427 bridge.setProtocols(protocols);
428 updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
429 null, bridgeUUID, bridge.getRow());
430 logger.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
431 protocols.toArray()[0],updateOperationStatus);
433 } catch (SchemaVersionMismatchException e){
434 logger.debug(e.toString());
437 // If we fail to update the protocols
438 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
439 return updateOperationStatus.isSuccess();
442 Status status = null;
443 UUID currControllerUuid = null;
444 InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
445 short ofControllerPort = getControllerOFPort();
446 String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
447 Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
448 newController.setTarget(newControllerTarget);
449 final String controllerTableName = newController.getSchema().getName();
451 currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
453 if (currControllerUuid != null) {
454 bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
455 bridge.setController(Sets.newHashSet(currControllerUuid));
456 status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
458 status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
461 if (status != null) {
462 return status.isSuccess();
469 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
470 if (connectionService == null) {
471 logger.error("Couldn't refer to the ConnectionService");
476 Connection connection = connectionService.getConnection(node);
477 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
479 Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
480 for (String uuid : brTableCache.keySet()) {
481 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
482 if (bridge.getName().contains(bridgeIdentifier)) {
483 return setOFController(node, uuid);
486 } catch(Exception e) {
487 logger.error("Error in setBridgeOFController()",e);
493 public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
494 Connection connection = connectionService.getConnection(node);
495 if (connection == null) return null;
496 OvsdbClient client = connection.getClient();
497 TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
498 if (typedTable == null) return null;
499 return typedTable.getSchema().getName();
503 public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
504 Connection connection = connectionService.getConnection(node);
505 if (connection == null) return null;
506 OvsdbClient client = connection.getClient();
507 return (T)client.getTypedRowWrapper(typedClass, row);
511 public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
512 Connection connection = connectionService.getConnection(node);
513 if (connection == null) return null;
514 OvsdbClient client = connection.getClient();
515 return client.createTypedRowWrapper(typedClass);
518 // SCHEMA-INDEPENDENT Configuration Service APIs
520 private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
521 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
522 if (cache == null) return null;
523 for (String tableName : cache.keySet()) {
524 ConcurrentMap<String, Row> rows = cache.get(tableName);
525 if (rows.get(rowUuid.toString()) != null) {
532 private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
533 Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
534 String refColumn = null;
535 for (String columnName : columnSchemas.keySet()) {
536 ColumnSchema columnSchema = columnSchemas.get(columnName);
537 if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
538 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
539 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
540 if (refColumn == null) {
541 refColumn = columnName;
543 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
548 if (refColumn != null) {
551 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
554 * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
555 * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
557 private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
558 String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
559 Row<GenericTableSchema> row,
560 TransactionBuilder transactionBuilder) {
561 // Insert the row as the first transaction entry
562 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
563 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
564 transactionBuilder.add(op.insert(childTableSchema, row)
567 // Followed by the Mutation
568 if (parentColumn != null) {
569 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
570 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
571 ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
574 .add(op.mutate(parentTableSchema)
575 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
576 .where(_uuid.opEqual(parentUuid))
582 * insert a Row in a Table of a specified Database Schema.
584 * This method can insert just a single Row specified in the row parameter.
585 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
586 * can insert a hierarchy of rows with parent-child relationship.
588 * @param node OVSDB Node
589 * @param databaseName Database Name that represents the Schema supported by the node.
590 * @param tableName Table on which the row is inserted
591 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
592 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
593 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
594 * @param row Row of table Content to be inserted
595 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
596 * @return UUID of the inserted Row
599 public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
600 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
601 Connection connection = connectionService.getConnection(node);
602 OvsdbClient client = connection.getClient();
603 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
604 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
606 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
608 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
609 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
610 return uuid.getData();
614 * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
615 * {@link insertRow(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertRow}
616 * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
617 * the necessary service to populate the Parent Table Name and Parent Column Name.
619 * This method can insert just a single Row specified in the row parameter.
620 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
621 * can insert a hierarchy of rows with parent-child relationship.
623 * @param node OVSDB Node
624 * @param databaseName Database Name that represents the Schema supported by the node.
625 * @param tableName Table on which the row is inserted
626 * @param parentUuid UUID of the parent table to which this operation will result in attaching/mutating.
627 * @param row Row of table Content to be inserted
628 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
629 * @return UUID of the inserted Row
632 public UUID insertRow(Node node, String databaseName, String tableName,
633 UUID parentRowUuid, Row<GenericTableSchema> row)
634 throws OvsdbPluginException {
635 return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
639 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
641 * @param node OVSDB Node
642 * @param databaseName Database Name that represents the Schema supported by the node.
643 * @param tableName Table on which the row is inserted
644 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
645 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
646 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
647 * @param row Row Tree with parent-child relationships via column of type refTable.
648 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
649 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
652 public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
653 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
654 Connection connection = connectionService.getConnection(node);
655 OvsdbClient client = connection.getClient();
657 if (databaseName == null || tableName == null) {
658 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
661 if (parentTable == null && parentUuid != null) {
662 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
665 if (parentColumn == null && parentTable != null) {
666 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
667 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
668 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
671 logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
672 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
674 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
675 extractReferencedRows(node, databaseName, row, referencedRows, 0);
676 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
677 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
679 String namedUuid = "Transaction_"+ tableName;
680 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
681 parentColumn, namedUuid, row, transactionBuilder);
683 int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
684 // Insert Referenced Rows
685 if (referencedRows != null) {
686 for (UUID refUuid : referencedRows.keySet()) {
687 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
688 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
689 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
690 .withId(refUuid.toString()));
694 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
695 List<OperationResult> operationResults;
697 operationResults = results.get();
698 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
699 throw new OvsdbPluginException("Insert Operation Failed");
701 for (OperationResult result : operationResults) {
702 if (result.getError() != null) {
703 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
706 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
707 } catch (InterruptedException | ExecutionException e) {
708 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
713 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
714 * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertTree}
716 * @param node OVSDB Node
717 * @param databaseName Database Name that represents the Schema supported by the node.
718 * @param tableName Table on which the row is inserted
719 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
720 * @param row Row Tree with parent-child relationships via column of type refTable.
721 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
722 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
725 public Row<GenericTableSchema> insertTree(Node node, String databaseName,
726 String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
727 throws OvsdbPluginException {
728 return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
732 * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
733 * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
734 * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
735 * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
736 * entire row and this method will help navigate it through and identify such cases.
737 * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
738 * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
739 * Mutation on the parent rows.
741 * @param node OVSDB Node
742 * @param dbName Database Name that represents the Schema supported by the node.
743 * @param row Row Tree with parent-child relationships via column of type refTable.
744 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
745 * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
746 * This index will help to retain the uniqueness.
748 private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
749 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
750 int namedUuidSuffix) {
751 OvsdbClient client = connectionService.getConnection(node).getClient();
752 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
753 for (Column column : columns) {
754 if (column.getData() != null) {
755 if (column.getData() instanceof ReferencedRow) {
756 ReferencedRow refRowObject = (ReferencedRow)column.getData();
757 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
758 column.setData(refUuid);
760 DatabaseSchema dbSchema = client.getSchema(dbName).get();
761 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
762 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
763 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
764 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
765 } catch (InterruptedException | ExecutionException e) {
766 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
768 } else if (column.getData() instanceof OvsdbSet) {
769 OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
770 OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
771 for (Object obj : setObject) {
772 if (obj instanceof ReferencedRow) {
773 ReferencedRow refRowObject = (ReferencedRow)obj;
774 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
775 modifiedSet.add(refUuid);
777 DatabaseSchema dbSchema = client.getSchema(dbName).get();
778 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
779 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
780 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
781 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
782 } catch (InterruptedException | ExecutionException e) {
783 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
786 modifiedSet.add(obj);
789 column.setData(modifiedSet);
796 * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
797 * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
798 * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
799 * the Row to the application.
801 * @param dbSchema Database Schema supported by the node.
802 * @param row Row Tree with parent-child relationships via column of type refTable.
803 * @param tableName Table on which the row is inserted
804 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
805 * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
806 * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
809 private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
810 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
811 List<OperationResult> operationResults, int referencedRowsInsertIndex) {
812 UUID primaryRowUuid = operationResults.get(0).getUuid();
813 TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
814 ColumnSchema<GenericTableSchema, UUID> _uuid = primaryRowTableSchema.column("_uuid", UUID.class);
816 Column<GenericTableSchema, UUID> _uuidColumn = new Column<GenericTableSchema, UUID>(_uuid, primaryRowUuid);
817 row.addColumn("_uuid", _uuidColumn);
820 if (referencedRows != null) {
821 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
822 if (referencedRows != null) {
823 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
824 UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
825 for (Column column : columns) {
826 if (column.getData() != null) {
827 if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
828 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
829 } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
830 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
831 refSet.remove(refUuid);
832 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
843 public Row<GenericTableSchema> updateRow(Node node, String databaseName,
844 String tableName, UUID rowUuid, Row<GenericTableSchema> row,
845 boolean overwrite) throws OvsdbPluginException {
846 Connection connection = connectionService.getConnection(node);
847 OvsdbClient client = connection.getClient();
849 logger.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
850 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
852 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
853 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
854 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
855 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
856 transactionBuilder.add(op.update(tableSchema, row)
857 .where(_uuid.opEqual(rowUuid))
860 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
861 List<OperationResult> operationResults = results.get();
862 for (OperationResult result : operationResults) {
863 if (result.getError() != null) {
864 throw new OvsdbPluginException("Error updating row : " + result.getError() +
865 " Details: " + result.getDetails());
868 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
869 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
872 return this.getRow(node, databaseName, tableName, rowUuid);
873 } catch(Exception e){
874 throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
879 public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
880 String parentColumn, UUID rowUuid) throws OvsdbPluginException {
881 Connection connection = connectionService.getConnection(node);
882 OvsdbClient client = connection.getClient();
884 if (parentTable == null && parentRowUuid != null) {
885 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
888 if (parentColumn == null && parentTable != null) {
889 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
890 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
891 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
894 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
895 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
897 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
898 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
899 this.processDeleteTransaction(client, databaseName, tableName,
900 parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
902 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
903 List<OperationResult> operationResults;
905 operationResults = results.get();
906 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
907 throw new OvsdbPluginException("Delete Operation Failed");
909 for (OperationResult result : operationResults) {
910 if (result.getError() != null) {
911 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
914 } catch (InterruptedException | ExecutionException e) {
915 // TODO Auto-generated catch block
921 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
922 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
926 public Row<GenericTableSchema> getRow(Node node, String databaseName,
927 String tableName, UUID uuid) throws OvsdbPluginException {
928 ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
930 return rows.get(uuid);
936 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
937 String databaseName, String tableName) throws OvsdbPluginException {
938 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
939 if (ovsTable == null) return null;
940 ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
941 for (String uuidStr : ovsTable.keySet()) {
942 tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
948 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
949 String databaseName, String tableName, String fiqlQuery)
950 throws OvsdbPluginException {
951 return this.getRows(node, databaseName, tableName);
955 public List<String> getTables(Node node, String databaseName) throws OvsdbPluginException {
956 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
957 if (cache == null) return null;
958 return new ArrayList<String>(cache.keySet());