2 * Copyright (C) 2013 Red Hat, Inc.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 * Authors : Madhu Venugopal, Brent Salisbury, Keith Burns
10 package org.opendaylight.ovsdb.plugin.impl;
12 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.AbstractMap;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.ExecutionException;
25 import org.eclipse.osgi.framework.console.CommandProvider;
26 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
27 import org.opendaylight.controller.sal.core.Node;
28 import org.opendaylight.controller.sal.utils.Status;
29 import org.opendaylight.controller.sal.utils.StatusCode;
30 import org.opendaylight.ovsdb.lib.OvsdbClient;
31 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
32 import org.opendaylight.ovsdb.lib.notation.Column;
33 import org.opendaylight.ovsdb.lib.notation.Mutator;
34 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
35 import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
36 import org.opendaylight.ovsdb.lib.notation.Row;
37 import org.opendaylight.ovsdb.lib.notation.UUID;
38 import org.opendaylight.ovsdb.lib.operations.Insert;
39 import org.opendaylight.ovsdb.lib.operations.Operation;
40 import org.opendaylight.ovsdb.lib.operations.OperationResult;
41 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
42 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
43 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
44 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
45 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
46 import org.opendaylight.ovsdb.lib.schema.TableSchema;
47 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
48 import org.opendaylight.ovsdb.plugin.api.Connection;
49 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
50 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
51 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
52 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryService;
53 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
54 import org.opendaylight.ovsdb.plugin.error.OvsdbPluginException;
55 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
56 import org.opendaylight.ovsdb.schema.openvswitch.Controller;
57 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
58 import org.opendaylight.ovsdb.schema.openvswitch.Port;
59 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
61 import org.osgi.framework.BundleContext;
62 import org.osgi.framework.FrameworkUtil;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 import com.fasterxml.jackson.databind.node.ObjectNode;
67 import com.google.common.collect.Maps;
68 import com.google.common.collect.Sets;
69 import com.google.common.util.concurrent.ListenableFuture;
71 public class ConfigurationServiceImpl implements OvsdbConfigurationService
73 private static final Logger logger = LoggerFactory
74 .getLogger(ConfigurationServiceImpl.class);
76 OvsdbConnectionService connectionService;
77 OvsdbInventoryService ovsdbInventoryService;
78 protected static final String OPENFLOW_10 = "1.0";
79 protected static final String OPENFLOW_13 = "1.3";
85 * Function called by the dependency manager when at least one dependency
86 * become unsatisfied or when the component is shutting down because for
87 * example bundle is being stopped.
94 * Function called by dependency manager after "init ()" is called and after
95 * the services provided by the class are registered in the service registry
99 registerWithOSGIConsole();
102 private void registerWithOSGIConsole() {
103 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
105 bundleContext.registerService(CommandProvider.class.getName(), this,
110 * Function called by the dependency manager before the services exported by
111 * the component are unregistered, this will be followed by a "destroy ()"
118 public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
119 this.connectionService = connectionService;
122 public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
123 if (this.connectionService == connectionService) {
124 this.connectionService = null;
128 public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
129 this.ovsdbInventoryService = ovsdbInventoryService;
132 public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
133 if (this.ovsdbInventoryService == ovsdbInventoryService) {
134 this.ovsdbInventoryService = null;
138 private IClusterGlobalServices clusterServices;
140 public void setClusterServices(IClusterGlobalServices i) {
141 this.clusterServices = i;
144 public void unsetClusterServices(IClusterGlobalServices i) {
145 if (this.clusterServices == i) {
146 this.clusterServices = null;
150 private Connection getConnection (Node node) {
151 Connection connection = connectionService.getConnection(node);
152 if (connection == null || !connection.getClient().isActive()) {
159 * There are a few Open_vSwitch schema specific special case handling to be done for
160 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
163 private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
164 String tableName, String uuid, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
165 Port port = client.getTypedRowWrapper(Port.class, null);
166 if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
167 port = client.getTypedRowWrapper(Port.class, row);
168 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
169 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
170 ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
171 String namedUuid = "Special_"+tableName;
172 List<Operation> priorOperations = transactionBuilder.getOperations();
173 Insert portOperation = (Insert)priorOperations.get(0);
174 portOperation.value(columnSchema, new UUID(namedUuid));
176 Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
177 List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
178 columns.add(nameColumn);
179 Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
180 this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
185 * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
186 * the parent table for the newly inserted Child.
187 * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
189 private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
190 String parentTable, String parentUuid, String parentColumn, String namedUuid,
191 Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
192 this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
193 namedUuid, row, transactionBuilder);
195 * There are a few Open_vSwitch schema specific special case handling to be done for
196 * the older API (such as by inserting a mandatory Interface row automatically upon inserting
199 handleSpecialInsertCase(client, databaseName, childTable, namedUuid, row, transactionBuilder);
203 * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
204 * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
205 * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
208 public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
209 if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) return null;
210 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
211 if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
212 Connection connection = connectionService.getConnection(node);
213 OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
214 ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
215 if (row == null || row.size() == 0) return null;
216 return (String)row.keySet().toArray()[0];
222 * Though this is a New API that takes in Row object, this still is considered a
223 * Deprecated call because of the assumption with a Single Row insertion.
224 * An ideal insertRow must be able to take in multiple Rows, which includes the
225 * Row being inserted in one Table and other Rows that needs mutate in other Tables.
229 public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
230 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
231 if (parentColumn == null) {
232 parentColumn = new String[]{null, null};
235 Connection connection = connectionService.getConnection(node);
236 OvsdbClient client = connection.getClient();
238 if (parentUuid == null) {
239 parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
241 logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
242 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
244 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
245 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
247 String namedUuid = "Transaction_"+ tableName;
248 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
249 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
250 row, transactionBuilder);
252 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
253 List<OperationResult> operationResults;
255 operationResults = results.get();
256 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
257 return new StatusWithUuid(StatusCode.INTERNALERROR);
259 for (OperationResult result : operationResults) {
260 if (result.getError() != null) {
261 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
264 UUID uuid = operationResults.get(0).getUuid();
265 return new StatusWithUuid(StatusCode.SUCCESS, uuid);
266 } catch (InterruptedException | ExecutionException e) {
267 // TODO Auto-generated catch block
268 return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
275 public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
276 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
277 Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
278 return new StatusWithUuid(StatusCode.SUCCESS);
281 private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
282 String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
283 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
284 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
286 if (parentColumn != null) {
287 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
288 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
290 .add(op.mutate(parentTableSchema)
291 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
292 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
296 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
297 transactionBuilder.add(op.delete(childTableSchema)
298 .where(_uuid.opEqual(new UUID(uuid)))
304 public Status deleteRow(Node node, String tableName, String uuid) {
305 String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
306 Connection connection = connectionService.getConnection(node);
307 OvsdbClient client = connection.getClient();
309 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
310 if (parentColumn == null) {
311 parentColumn = new String[]{null, null};
314 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
315 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
317 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
318 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
319 this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
320 parentColumn[0], parentColumn[1], uuid, transactionBuilder);
322 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
323 List<OperationResult> operationResults;
325 operationResults = results.get();
326 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
327 return new StatusWithUuid(StatusCode.INTERNALERROR);
329 for (OperationResult result : operationResults) {
330 if (result.getError() != null) {
331 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
334 } catch (InterruptedException | ExecutionException e) {
335 logger.error("Error in deleteRow() {} {}", node, tableName, e);
338 return new Status(StatusCode.SUCCESS);
343 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
344 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
350 public Row getRow(Node node, String tableName, String uuid) {
351 Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
352 if (ovsTable == null) return null;
353 return ovsTable.get(uuid);
358 public List<String> getTables(Node node) {
359 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
362 private InetAddress getControllerIPAddress(Connection connection) {
363 InetAddress controllerIP = null;
365 String addressString = ConfigProperties.getProperty(this.getClass(), "ovsdb.controller.address");
367 if (addressString != null) {
369 controllerIP = InetAddress.getByName(addressString);
370 if (controllerIP != null) {
373 } catch (UnknownHostException e) {
374 logger.error("Host {} is invalid", addressString);
378 addressString = ConfigProperties.getProperty(this.getClass(), "of.address");
380 if (addressString != null) {
382 controllerIP = InetAddress.getByName(addressString);
383 if (controllerIP != null) {
386 } catch (UnknownHostException e) {
387 logger.error("Host {} is invalid", addressString);
392 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
394 } catch (Exception e) {
395 logger.debug("Invalid connection provided to getControllerIPAddresses", e);
400 private short getControllerOFPort() {
401 Short defaultOpenFlowPort = 6633;
402 Short openFlowPort = defaultOpenFlowPort;
403 String portString = ConfigProperties.getProperty(this.getClass(), "of.listenPort");
404 if (portString != null) {
406 openFlowPort = Short.decode(portString).shortValue();
407 } catch (NumberFormatException e) {
408 logger.warn("Invalid port:{}, use default({})", portString,
415 private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
416 ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
419 for (Map.Entry<String, Row> entry : rows.entrySet()) {
420 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
421 Column<GenericTableSchema, String> column = currController.getTargetColumn();
422 String currTarget = column.getData();
423 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
424 return currController.getUuid();
432 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
433 Connection connection = this.getConnection(node);
434 if (connection == null) {
438 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
440 Status updateOperationStatus = null;
442 OvsdbSet<String> protocols = new OvsdbSet<String>();
444 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
447 protocols.add("OpenFlow10");
452 protocols.add("OpenFlow13");
455 bridge.setProtocols(protocols);
456 updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
457 null, bridgeUUID, bridge.getRow());
458 logger.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
459 protocols.toArray()[0],updateOperationStatus);
461 } catch (SchemaVersionMismatchException e){
462 logger.debug(e.toString());
465 // If we fail to update the protocols
466 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
467 return updateOperationStatus.isSuccess();
470 Status status = null;
471 UUID currControllerUuid = null;
472 InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
473 short ofControllerPort = getControllerOFPort();
474 String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
475 Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
476 newController.setTarget(newControllerTarget);
477 final String controllerTableName = newController.getSchema().getName();
479 currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
481 if (currControllerUuid != null) {
482 bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
483 bridge.setController(Sets.newHashSet(currControllerUuid));
484 status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
486 status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
489 if (status != null) {
490 return status.isSuccess();
497 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
498 if (connectionService == null) {
499 logger.error("Couldn't refer to the ConnectionService");
504 Connection connection = connectionService.getConnection(node);
505 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
507 Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
508 for (String uuid : brTableCache.keySet()) {
509 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
510 if (bridge.getName().contains(bridgeIdentifier)) {
511 return setOFController(node, uuid);
514 } catch(Exception e) {
515 logger.error("Error in setBridgeOFController()",e);
521 public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
522 Connection connection = connectionService.getConnection(node);
523 if (connection == null) return null;
524 OvsdbClient client = connection.getClient();
525 TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
526 if (typedTable == null) return null;
527 return typedTable.getSchema().getName();
531 public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
532 Connection connection = connectionService.getConnection(node);
533 if (connection == null) return null;
534 OvsdbClient client = connection.getClient();
535 return (T)client.getTypedRowWrapper(typedClass, row);
539 public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
540 Connection connection = connectionService.getConnection(node);
541 if (connection == null) return null;
542 OvsdbClient client = connection.getClient();
543 return client.createTypedRowWrapper(typedClass);
546 // SCHEMA-INDEPENDENT Configuration Service APIs
548 private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
549 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
550 if (cache == null) return null;
551 for (String tableName : cache.keySet()) {
552 ConcurrentMap<String, Row> rows = cache.get(tableName);
553 if (rows.get(rowUuid.toString()) != null) {
560 private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
561 Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
562 String refColumn = null;
563 for (String columnName : columnSchemas.keySet()) {
564 ColumnSchema columnSchema = columnSchemas.get(columnName);
565 if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
566 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
567 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
568 if (refColumn == null) {
569 refColumn = columnName;
571 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
576 if (refColumn != null) {
579 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
582 * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
583 * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
585 private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
586 String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
587 Row<GenericTableSchema> row,
588 TransactionBuilder transactionBuilder) {
589 // Insert the row as the first transaction entry
590 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
591 TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
592 transactionBuilder.add(op.insert(childTableSchema, row)
595 // Followed by the Mutation
596 if (parentColumn != null) {
597 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
598 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
599 ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
602 .add(op.mutate(parentTableSchema)
603 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
604 .where(_uuid.opEqual(parentUuid))
610 * insert a Row in a Table of a specified Database Schema.
612 * This method can insert just a single Row specified in the row parameter.
613 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
614 * can insert a hierarchy of rows with parent-child relationship.
616 * @param node OVSDB Node
617 * @param databaseName Database Name that represents the Schema supported by the node.
618 * @param tableName Table on which the row is inserted
619 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
620 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
621 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
622 * @param row Row of table Content to be inserted
623 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
624 * @return UUID of the inserted Row
627 public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
628 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
629 Connection connection = connectionService.getConnection(node);
630 OvsdbClient client = connection.getClient();
631 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
632 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
634 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
636 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
637 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
638 return uuid.getData();
642 * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
643 * {@link insertRow(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertRow}
644 * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
645 * the necessary service to populate the Parent Table Name and Parent Column Name.
647 * This method can insert just a single Row specified in the row parameter.
648 * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
649 * can insert a hierarchy of rows with parent-child relationship.
651 * @param node OVSDB Node
652 * @param databaseName Database Name that represents the Schema supported by the node.
653 * @param tableName Table on which the row is inserted
654 * @param parentUuid UUID of the parent table to which this operation will result in attaching/mutating.
655 * @param row Row of table Content to be inserted
656 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
657 * @return UUID of the inserted Row
660 public UUID insertRow(Node node, String databaseName, String tableName,
661 UUID parentRowUuid, Row<GenericTableSchema> row)
662 throws OvsdbPluginException {
663 return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
667 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
669 * @param node OVSDB Node
670 * @param databaseName Database Name that represents the Schema supported by the node.
671 * @param tableName Table on which the row is inserted
672 * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
673 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
674 * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
675 * @param row Row Tree with parent-child relationships via column of type refTable.
676 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
677 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
680 public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
681 String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
682 Connection connection = connectionService.getConnection(node);
683 OvsdbClient client = connection.getClient();
685 if (databaseName == null || tableName == null) {
686 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
689 if (parentTable == null && parentUuid != null) {
690 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
693 if (parentColumn == null && parentTable != null) {
694 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
695 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
696 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
699 logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
700 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
702 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
703 extractReferencedRows(node, databaseName, row, referencedRows, 0);
704 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
705 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
707 String namedUuid = "Transaction_"+ tableName;
708 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
709 parentColumn, namedUuid, row, transactionBuilder);
711 int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
712 // Insert Referenced Rows
713 if (referencedRows != null) {
714 for (UUID refUuid : referencedRows.keySet()) {
715 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
716 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
717 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
718 .withId(refUuid.toString()));
722 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
723 List<OperationResult> operationResults;
725 operationResults = results.get();
726 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
727 throw new OvsdbPluginException("Insert Operation Failed");
729 for (OperationResult result : operationResults) {
730 if (result.getError() != null) {
731 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
734 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
735 } catch (InterruptedException | ExecutionException e) {
736 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
741 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
742 * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertTree}
744 * @param node OVSDB Node
745 * @param databaseName Database Name that represents the Schema supported by the node.
746 * @param tableName Table on which the row is inserted
747 * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
748 * @param row Row Tree with parent-child relationships via column of type refTable.
749 * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
750 * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
753 public Row<GenericTableSchema> insertTree(Node node, String databaseName,
754 String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
755 throws OvsdbPluginException {
756 return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
760 * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
761 * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
762 * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
763 * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
764 * entire row and this method will help navigate it through and identify such cases.
765 * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
766 * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
767 * Mutation on the parent rows.
769 * @param node OVSDB Node
770 * @param dbName Database Name that represents the Schema supported by the node.
771 * @param row Row Tree with parent-child relationships via column of type refTable.
772 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
773 * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
774 * This index will help to retain the uniqueness.
776 private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
777 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
778 int namedUuidSuffix) {
779 OvsdbClient client = connectionService.getConnection(node).getClient();
780 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
781 for (Column column : columns) {
782 if (column.getData() != null) {
783 if (column.getData() instanceof ReferencedRow) {
784 ReferencedRow refRowObject = (ReferencedRow)column.getData();
785 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
786 column.setData(refUuid);
788 DatabaseSchema dbSchema = client.getSchema(dbName).get();
789 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
790 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
791 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
792 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
793 } catch (InterruptedException | ExecutionException e) {
794 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
796 } else if (column.getData() instanceof OvsdbSet) {
797 OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
798 OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
799 for (Object obj : setObject) {
800 if (obj instanceof ReferencedRow) {
801 ReferencedRow refRowObject = (ReferencedRow)obj;
802 UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
803 modifiedSet.add(refUuid);
805 DatabaseSchema dbSchema = client.getSchema(dbName).get();
806 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
807 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
808 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
809 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
810 } catch (InterruptedException | ExecutionException e) {
811 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
814 modifiedSet.add(obj);
817 column.setData(modifiedSet);
824 * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
825 * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
826 * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
827 * the Row to the application.
829 * @param dbSchema Database Schema supported by the node.
830 * @param row Row Tree with parent-child relationships via column of type refTable.
831 * @param tableName Table on which the row is inserted
832 * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
833 * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
834 * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
837 private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
838 Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
839 List<OperationResult> operationResults, int referencedRowsInsertIndex) {
840 UUID primaryRowUuid = operationResults.get(0).getUuid();
841 TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
842 ColumnSchema<GenericTableSchema, UUID> _uuid = primaryRowTableSchema.column("_uuid", UUID.class);
844 Column<GenericTableSchema, UUID> _uuidColumn = new Column<GenericTableSchema, UUID>(_uuid, primaryRowUuid);
845 row.addColumn("_uuid", _uuidColumn);
848 if (referencedRows != null) {
849 Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
850 if (referencedRows != null) {
851 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
852 UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
853 for (Column column : columns) {
854 if (column.getData() != null) {
855 if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
856 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
857 } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
858 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
859 refSet.remove(refUuid);
860 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
871 public Row<GenericTableSchema> updateRow(Node node, String databaseName,
872 String tableName, UUID rowUuid, Row<GenericTableSchema> row,
873 boolean overwrite) throws OvsdbPluginException {
874 Connection connection = connectionService.getConnection(node);
875 OvsdbClient client = connection.getClient();
877 logger.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
878 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
880 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
881 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
882 TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
883 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
884 transactionBuilder.add(op.update(tableSchema, row)
885 .where(_uuid.opEqual(rowUuid))
888 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
889 List<OperationResult> operationResults = results.get();
890 for (OperationResult result : operationResults) {
891 if (result.getError() != null) {
892 throw new OvsdbPluginException("Error updating row : " + result.getError() +
893 " Details: " + result.getDetails());
896 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
897 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
900 return this.getRow(node, databaseName, tableName, rowUuid);
901 } catch(Exception e){
902 throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
907 public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
908 String parentColumn, UUID rowUuid) throws OvsdbPluginException {
909 Connection connection = connectionService.getConnection(node);
910 OvsdbClient client = connection.getClient();
912 if (parentTable == null && parentRowUuid != null) {
913 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
916 if (parentColumn == null && parentTable != null) {
917 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
918 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
919 parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
922 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
923 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
925 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
926 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
927 this.processDeleteTransaction(client, databaseName, tableName,
928 parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
930 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
931 List<OperationResult> operationResults;
933 operationResults = results.get();
934 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
935 throw new OvsdbPluginException("Delete Operation Failed");
937 for (OperationResult result : operationResults) {
938 if (result.getError() != null) {
939 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
942 } catch (InterruptedException | ExecutionException e) {
943 // TODO Auto-generated catch block
949 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
950 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
954 public Row<GenericTableSchema> getRow(Node node, String databaseName,
955 String tableName, UUID uuid) throws OvsdbPluginException {
956 ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
958 return rows.get(uuid);
964 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
965 String databaseName, String tableName) throws OvsdbPluginException {
966 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
967 if (ovsTable == null) return null;
968 ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
969 for (String uuidStr : ovsTable.keySet()) {
970 tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
976 public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
977 String databaseName, String tableName, String fiqlQuery)
978 throws OvsdbPluginException {
979 return this.getRows(node, databaseName, tableName);
983 public List<String> getTables(Node node, String databaseName) throws OvsdbPluginException {
984 ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
985 if (cache == null) return null;
986 return new ArrayList<String>(cache.keySet());