Schema independent plugin insert operation for a traditional single Row & more advanc... 51/10351/1
authorMadhu Venugopal <mavenugo@gmail.com>
Wed, 27 Aug 2014 01:20:46 +0000 (18:20 -0700)
committerMadhu Venugopal <mavenugo@gmail.com>
Wed, 27 Aug 2014 01:20:46 +0000 (18:20 -0700)
This commit brings in the plugin support which will be used by northboundv3 APIs & more advanced applications that make use of the plugin.

Change-Id: If57d82da9234cc1d5a40ffd9b789ec3a81092383
Signed-off-by: Madhu Venugopal <mavenugo@gmail.com>
library/src/main/java/org/opendaylight/ovsdb/lib/notation/ReferencedRow.java [new file with mode: 0644]
library/src/main/java/org/opendaylight/ovsdb/lib/schema/BaseType.java
plugin/src/main/java/org/opendaylight/ovsdb/plugin/impl/ConfigurationServiceImpl.java

diff --git a/library/src/main/java/org/opendaylight/ovsdb/lib/notation/ReferencedRow.java b/library/src/main/java/org/opendaylight/ovsdb/lib/notation/ReferencedRow.java
new file mode 100644 (file)
index 0000000..f8d09d5
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 Red Hat, Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Authors : Madhu Venugopal
+ */
+
+package org.opendaylight.ovsdb.lib.notation;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class ReferencedRow {
+    String refTable;
+    JsonNode jsonNode;
+    public ReferencedRow(String refTable, JsonNode jsonNode) {
+        this.refTable = refTable;
+        this.jsonNode = jsonNode;
+    }
+
+    public JsonNode getJsonNode() {
+        return jsonNode;
+    }
+
+    public String getRefTable() {
+        return refTable;
+    }
+}
index be9149f3a2cdebca5882e8017045677dc5fe72ba..070de83249ff8347e7a48f08e9b8353ffa6bb4c2 100644 (file)
@@ -12,6 +12,7 @@ package org.opendaylight.ovsdb.lib.schema;
 import java.util.Set;
 
 import org.opendaylight.ovsdb.lib.error.TyperException;
+import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
 import org.opendaylight.ovsdb.lib.notation.UUID;
 
 import com.fasterxml.jackson.databind.JsonNode;
@@ -478,6 +479,14 @@ public abstract class BaseType<E extends BaseType<E>> {
                         return new UUID(value.get(1).asText());
                     }
                 }
+            } else {
+                /*
+                 * UUIDBaseType used by RefTable from SouthBound will always be an Array of ["uuid", <uuid>].
+                 * But there are some cases from northbound where the RefTable type can be expanded to a Row
+                 * with contents. In those scenarios, just retain the content and return a ReferencedRow for
+                 * the upper layer functions to process it.
+                 */
+                return new ReferencedRow(refTable, value);
             }
             return null;
         }
index 56aea7e9fd0f89f008704d41f7a767bdc230174b..90e02948f7f502108e445f7df67d600d46870821 100644 (file)
@@ -13,7 +13,9 @@ import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.AbstractMap;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -37,6 +39,7 @@ import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
 import org.opendaylight.ovsdb.lib.notation.Column;
 import org.opendaylight.ovsdb.lib.notation.Mutator;
 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
+import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
 import org.opendaylight.ovsdb.lib.notation.Row;
 import org.opendaylight.ovsdb.lib.notation.UUID;
 import org.opendaylight.ovsdb.lib.operations.Insert;
@@ -67,7 +70,9 @@ import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.ListenableFuture;
 
 public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigService,
@@ -183,7 +188,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
             List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
             columns.add(nameColumn);
             Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
-            this.processInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
+            this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
         }
     }
 
@@ -192,25 +197,11 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
      * the parent table for the newly inserted Child.
      * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
      */
-    private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
+    private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
                                     String parentTable, String parentUuid, String parentColumn, String namedUuid,
                                     Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
-        DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
-        TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
-        transactionBuilder.add(op.insert(childTableSchema, row)
-                        .withId(namedUuid));
-
-        if (parentColumn != null) {
-            TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
-            ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
-            ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
-
-            transactionBuilder
-                .add(op.mutate(parentTableSchema)
-                        .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
-                        .where(_uuid.opEqual(new UUID(parentUuid)))
-                        .build());
-        }
+        this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
+                                      namedUuid, row, transactionBuilder);
         /*
          * There are a few Open_vSwitch schema specific special case handling to be done for
          * the older API (such as by inserting a mandatory Interface row automatically upon inserting
@@ -245,6 +236,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
      * Row being inserted in one Table and other Rows that needs mutate in other Tables.
      */
     @Override
+    @Deprecated
     public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
         if (parentColumn == null) {
@@ -264,7 +256,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
 
         String namedUuid = "Transaction_"+ tableName;
-        this.processInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
+        this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
                                 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
                                 row, transactionBuilder);
 
@@ -290,6 +282,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
     }
 
     @Override
+    @Deprecated
     public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
         Connection connection = connectionService.getConnection(node);
@@ -345,6 +338,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
     }
 
     @Override
+    @Deprecated
     public Status deleteRow(Node node, String tableName, String uuid) {
         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
         Connection connection = connectionService.getConnection(node);
@@ -384,12 +378,14 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
     }
 
     @Override
+    @Deprecated
     public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
         ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
         return ovsTable;
     }
 
     @Override
+    @Deprecated
     public Row getRow(Node node, String tableName, String uuid) {
         Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
         if (ovsTable == null) return null;
@@ -397,6 +393,7 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
     }
 
     @Override
+    @Deprecated
     public List<String> getTables(Node node) {
         ConcurrentMap<String, ConcurrentMap<String, Row>> cache  = ovsdbInventoryService.getCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
         if (cache == null) return null;
@@ -1175,30 +1172,285 @@ public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigServ
         return null;
     }
 
+
+    // SCHEMA-INDEPENDENT Configuration Service APIs
+
+    /*
+     * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
+     * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
+     */
+    private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
+                                    String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
+                                    Row<GenericTableSchema> row,
+                                    TransactionBuilder transactionBuilder) {
+        // Insert the row as the first transaction entry
+        DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
+        TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
+        transactionBuilder.add(op.insert(childTableSchema, row)
+                        .withId(namedUuid));
+
+        // Followed by the Mutation
+        if (parentColumn != null) {
+            TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
+            ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
+            ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
+
+            transactionBuilder
+                .add(op.mutate(parentTableSchema)
+                        .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
+                        .where(_uuid.opEqual(parentUuid))
+                        .build());
+        }
+    }
+
+    /**
+     * insert a Row in a Table of a specified Database Schema.
+     *
+     * This method can insert just a single Row specified in the row parameter.
+     * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
+     * can insert a hierarchy of rows with parent-child relationship.
+     *
+     * @param node OVSDB Node
+     * @param databaseName Database Name that represents the Schema supported by the node.
+     * @param tableName Table on which the row is inserted
+     * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
+     * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
+     * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
+     * @param row Row of table Content to be inserted
+     * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
+     * @return UUID of the inserted Row
+     */
     @Override
     public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
                           String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
-        throw new OvsdbPluginException("Not implemented Yet");
+        Connection connection = connectionService.getConnection(node);
+        OvsdbClient client = connection.getClient();
+        DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
+        TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
+
+        Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
+
+        ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
+        Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
+        return uuid.getData();
     }
 
+    /**
+     * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
+     * {@link insertRow(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertRow}
+     * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
+     * the necessary service to populate the Parent Table Name and Parent Column Name.
+     *
+     * This method can insert just a single Row specified in the row parameter.
+     * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
+     * can insert a hierarchy of rows with parent-child relationship.
+     *
+     * @param node OVSDB Node
+     * @param databaseName Database Name that represents the Schema supported by the node.
+     * @param tableName Table on which the row is inserted
+     * @param parentUuid UUID of the parent table to which this operation will result in attaching/mutating.
+     * @param row Row of table Content to be inserted
+     * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
+     * @return UUID of the inserted Row
+     */
     @Override
     public UUID insertRow(Node node, String databaseName, String tableName,
             UUID parentRowUuid, Row<GenericTableSchema> row)
             throws OvsdbPluginException {
-        throw new OvsdbPluginException("Not implemented Yet");
+        return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
     }
 
+    /**
+     * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
+     *
+     * @param node OVSDB Node
+     * @param databaseName Database Name that represents the Schema supported by the node.
+     * @param tableName Table on which the row is inserted
+     * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
+     * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
+     * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
+     * @param row Row Tree with parent-child relationships via column of type refTable.
+     * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
+     * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
+     */
     @Override
-    public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
+    public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
                                               String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
-        throw new OvsdbPluginException("Not implemented Yet");
+        Connection connection = connectionService.getConnection(node);
+        OvsdbClient client = connection.getClient();
+
+        if (databaseName == null || tableName == null) {
+            throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
+        }
+        logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
+                     client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
+
+        Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
+        extractReferencedRows(node, databaseName, row, referencedRows, 0);
+        DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
+        TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
+
+        String namedUuid = "Transaction_"+ tableName;
+        this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
+                                      parentColumn, namedUuid, row, transactionBuilder);
+
+        int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
+        // Insert Referenced Rows
+        if (referencedRows != null) {
+            for (UUID refUuid : referencedRows.keySet()) {
+                Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
+                TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
+                transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
+                                .withId(refUuid.toString()));
+            }
+        }
+
+        ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
+        List<OperationResult> operationResults;
+        try {
+            operationResults = results.get();
+            if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
+                throw new OvsdbPluginException("Insert Operation Failed");
+            }
+            for (OperationResult result : operationResults) {
+                if (result.getError() != null) {
+                    throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
+                }
+            }
+            return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
+        } catch (InterruptedException | ExecutionException e) {
+            throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
+        }
     }
 
+    /**
+     * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
+     * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertTree}
+     *
+     * @param node OVSDB Node
+     * @param databaseName Database Name that represents the Schema supported by the node.
+     * @param tableName Table on which the row is inserted
+     * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
+     * @param row Row Tree with parent-child relationships via column of type refTable.
+     * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
+     * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
+     */
     @Override
     public Row<GenericTableSchema> insertTree(Node node, String databaseName,
             String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
             throws OvsdbPluginException {
-        throw new OvsdbPluginException("Not implemented Yet");
+        return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
+    }
+
+    /**
+     * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
+     * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
+     * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
+     * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
+     * entire row and this method will help navigate it through and identify such cases.
+     * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
+     * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
+     * Mutation on the parent rows.
+     *
+     * @param node OVSDB Node
+     * @param dbName Database Name that represents the Schema supported by the node.
+     * @param row Row Tree with parent-child relationships via column of type refTable.
+     * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
+     * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
+     *        This index will help to retain the uniqueness.
+     */
+    private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
+                                       Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
+                                       int namedUuidSuffix) {
+        OvsdbClient client = connectionService.getConnection(node).getClient();
+        Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
+        for (Column column : columns) {
+            if (column.getData() != null) {
+                if (column.getData() instanceof ReferencedRow) {
+                    ReferencedRow refRowObject = (ReferencedRow)column.getData();
+                    UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
+                    column.setData(refUuid);
+                    try {
+                        DatabaseSchema dbSchema = client.getSchema(dbName).get();
+                        GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
+                        Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
+                        referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
+                        extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
+                    } catch (InterruptedException | ExecutionException e) {
+                        logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
+                    }
+                } else if (column.getData() instanceof OvsdbSet) {
+                    OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
+                    OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
+                    for (Object obj : setObject) {
+                        if (obj instanceof ReferencedRow) {
+                            ReferencedRow refRowObject = (ReferencedRow)obj;
+                            UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
+                            modifiedSet.add(refUuid);
+                            try {
+                                DatabaseSchema dbSchema = client.getSchema(dbName).get();
+                                GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
+                                Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
+                                referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
+                                extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
+                            } catch (InterruptedException | ExecutionException e) {
+                                logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
+                            }
+                        } else {
+                            modifiedSet.add(obj);
+                        }
+                    }
+                    column.setData(modifiedSet);
+                }
+            }
+        }
+    }
+
+    /**
+     * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
+     * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
+     * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
+     * the Row to the application.
+     *
+     * @param dbSchema Database Schema supported by the node.
+     * @param row Row Tree with parent-child relationships via column of type refTable.
+     * @param tableName Table on which the row is inserted
+     * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
+     * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
+     * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
+     * @return
+     */
+    private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
+                                                     Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
+                                                     List<OperationResult> operationResults, int referencedRowsInsertIndex) {
+        UUID primaryRowUuid = operationResults.get(0).getUuid();
+        TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
+        ColumnSchema<GenericTableSchema, UUID> _uuid = primaryRowTableSchema.column("_uuid", UUID.class);
+        if (_uuid != null) {
+            Column<GenericTableSchema, UUID> _uuidColumn = new Column<GenericTableSchema, UUID>(_uuid, primaryRowUuid);
+            row.addColumn("_uuid", _uuidColumn);
+        }
+
+        if (referencedRows != null) {
+            Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
+            if (referencedRows != null) {
+                for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
+                    UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
+                    for (Column column : columns) {
+                        if (column.getData() != null) {
+                            if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
+                                column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
+                            } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
+                                OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
+                                refSet.remove(refUuid);
+                                refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return row;
     }
 
     @Override