Bug 1998 - Fixing Bridge Set-Controller operation failure
[ovsdb.git] / plugin / src / main / java / org / opendaylight / ovsdb / plugin / impl / ConfigurationServiceImpl.java
1 /*
2  * Copyright (C) 2013 Red Hat, Inc.
3  *
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
7  *
8  * Authors : Madhu Venugopal, Brent Salisbury, Keith Burns
9  */
10 package org.opendaylight.ovsdb.plugin.impl;
11
12 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
13
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.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ExecutionException;
26
27 import org.eclipse.osgi.framework.console.CommandInterpreter;
28 import org.eclipse.osgi.framework.console.CommandProvider;
29 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
30 import org.opendaylight.controller.sal.connection.ConnectionConstants;
31 import org.opendaylight.controller.sal.core.Node;
32 import org.opendaylight.controller.sal.core.NodeConnector;
33 import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
34 import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
35 import org.opendaylight.controller.sal.utils.Status;
36 import org.opendaylight.controller.sal.utils.StatusCode;
37 import org.opendaylight.ovsdb.lib.OvsdbClient;
38 import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
39 import org.opendaylight.ovsdb.lib.notation.Column;
40 import org.opendaylight.ovsdb.lib.notation.Mutator;
41 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
42 import org.opendaylight.ovsdb.lib.notation.ReferencedRow;
43 import org.opendaylight.ovsdb.lib.notation.Row;
44 import org.opendaylight.ovsdb.lib.notation.UUID;
45 import org.opendaylight.ovsdb.lib.operations.Insert;
46 import org.opendaylight.ovsdb.lib.operations.Operation;
47 import org.opendaylight.ovsdb.lib.operations.OperationResult;
48 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
49 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
50 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
51 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
52 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
53 import org.opendaylight.ovsdb.lib.schema.TableSchema;
54 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
55 import org.opendaylight.ovsdb.plugin.OvsdbConfigService;
56 import org.opendaylight.ovsdb.plugin.api.Connection;
57 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
58 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
59 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
60 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryService;
61 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
62 import org.opendaylight.ovsdb.plugin.error.OvsdbPluginException;
63 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
64 import org.opendaylight.ovsdb.schema.openvswitch.Controller;
65 import org.opendaylight.ovsdb.schema.openvswitch.Interface;
66 import org.opendaylight.ovsdb.schema.openvswitch.Manager;
67 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
68 import org.opendaylight.ovsdb.schema.openvswitch.Port;
69 import org.osgi.framework.BundleContext;
70 import org.osgi.framework.FrameworkUtil;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 import com.fasterxml.jackson.databind.node.ObjectNode;
75 import com.google.common.collect.ImmutableSet;
76 import com.google.common.collect.Maps;
77 import com.google.common.collect.Sets;
78 import com.google.common.util.concurrent.ListenableFuture;
79
80 public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigService,
81                                                  OvsdbConfigurationService,
82                                                  OvsdbConfigService,
83                                                  CommandProvider
84 {
85     private static final Logger logger = LoggerFactory
86             .getLogger(ConfigurationServiceImpl.class);
87
88     OvsdbConnectionService connectionService;
89     OvsdbInventoryService ovsdbInventoryService;
90     boolean forceConnect = false;
91     protected static final String OPENFLOW_10 = "1.0";
92     protected static final String OPENFLOW_13 = "1.3";
93
94     void init() {
95     }
96
97     /**
98      * Function called by the dependency manager when at least one dependency
99      * become unsatisfied or when the component is shutting down because for
100      * example bundle is being stopped.
101      *
102      */
103     void destroy() {
104     }
105
106     /**
107      * Function called by dependency manager after "init ()" is called and after
108      * the services provided by the class are registered in the service registry
109      *
110      */
111     void start() {
112         registerWithOSGIConsole();
113     }
114
115     private void registerWithOSGIConsole() {
116         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
117                 .getBundleContext();
118         bundleContext.registerService(CommandProvider.class.getName(), this,
119                 null);
120     }
121
122     /**
123      * Function called by the dependency manager before the services exported by
124      * the component are unregistered, this will be followed by a "destroy ()"
125      * calls
126      *
127      */
128     void stop() {
129     }
130
131     public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
132         this.connectionService = connectionService;
133     }
134
135     public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
136         if (this.connectionService == connectionService) {
137             this.connectionService = null;
138         }
139     }
140
141     public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
142         this.ovsdbInventoryService = ovsdbInventoryService;
143     }
144
145     public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
146         if (this.ovsdbInventoryService == ovsdbInventoryService) {
147             this.ovsdbInventoryService = null;
148         }
149     }
150
151     private IClusterGlobalServices clusterServices;
152
153     public void setClusterServices(IClusterGlobalServices i) {
154         this.clusterServices = i;
155     }
156
157     public void unsetClusterServices(IClusterGlobalServices i) {
158         if (this.clusterServices == i) {
159             this.clusterServices = null;
160         }
161     }
162
163     private Connection getConnection (Node node) {
164         Connection connection = connectionService.getConnection(node);
165         if (connection == null || !connection.getClient().isActive()) {
166             return null;
167         }
168
169         return connection;
170     }
171     /*
172      * There are a few Open_vSwitch schema specific special case handling to be done for
173      * the older API (such as by inserting a mandatory Interface row automatically upon inserting
174      * a Port row.
175      */
176     private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
177             String tableName, String uuid, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
178         Port port = client.getTypedRowWrapper(Port.class, null);
179         if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
180             port = client.getTypedRowWrapper(Port.class, row);
181             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
182             TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
183             ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
184             String namedUuid = "Special_"+tableName;
185             List<Operation> priorOperations = transactionBuilder.getOperations();
186             Insert portOperation = (Insert)priorOperations.get(0);
187             portOperation.value(columnSchema, new UUID(namedUuid));
188
189             Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
190             List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
191             columns.add(nameColumn);
192             Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
193             this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
194         }
195     }
196
197     /*
198      * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
199      * the parent table for the newly inserted Child.
200      * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
201      */
202     private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
203                                     String parentTable, String parentUuid, String parentColumn, String namedUuid,
204                                     Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
205         this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
206                                       namedUuid, row, transactionBuilder);
207         /*
208          * There are a few Open_vSwitch schema specific special case handling to be done for
209          * the older API (such as by inserting a mandatory Interface row automatically upon inserting
210          * a Port row.
211          */
212         handleSpecialInsertCase(client, databaseName, childTable, namedUuid, row, transactionBuilder);
213     }
214
215     /*
216      * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
217      * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
218      * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
219      * plugin layer.
220      */
221     public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
222         if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) return null;
223         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
224         if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
225             Connection connection = connectionService.getConnection(node);
226             OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
227             ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
228             if (row == null || row.size() == 0) return null;
229             return (String)row.keySet().toArray()[0];
230         }
231         return null;
232     }
233
234     /*
235      * Though this is a New API that takes in Row object, this still is considered a
236      * Deprecated call because of the assumption with a Single Row insertion.
237      * An ideal insertRow must be able to take in multiple Rows, which includes the
238      * Row being inserted in one Table and other Rows that needs mutate in other Tables.
239      */
240     @Override
241     @Deprecated
242     public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
243         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
244         if (parentColumn == null) {
245             parentColumn = new String[]{null, null};
246         }
247
248         Connection connection = connectionService.getConnection(node);
249         OvsdbClient client = connection.getClient();
250
251         if (parentUuid == null) {
252             parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
253         }
254         logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
255                      client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
256
257         DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
258         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
259
260         String namedUuid = "Transaction_"+ tableName;
261         this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
262                                 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
263                                 row, transactionBuilder);
264
265         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
266         List<OperationResult> operationResults;
267         try {
268             operationResults = results.get();
269             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
270                 return new StatusWithUuid(StatusCode.INTERNALERROR);
271             }
272             for (OperationResult result : operationResults) {
273                 if (result.getError() != null) {
274                     return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
275                 }
276             }
277             UUID uuid = operationResults.get(0).getUuid();
278             return new StatusWithUuid(StatusCode.SUCCESS, uuid);
279         } catch (InterruptedException | ExecutionException e) {
280             // TODO Auto-generated catch block
281             return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
282         }
283
284     }
285
286     @Override
287     @Deprecated
288     public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
289         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
290         Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
291         return new StatusWithUuid(StatusCode.SUCCESS);
292     }
293
294     private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
295                                     String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
296         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
297         TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
298
299         if (parentColumn != null) {
300             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
301             ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
302             transactionBuilder
303                 .add(op.mutate(parentTableSchema)
304                         .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
305                         .where(parentColumnSchema.opIncludes(new UUID(uuid)))
306                         .build());
307         }
308
309         ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
310         transactionBuilder.add(op.delete(childTableSchema)
311                 .where(_uuid.opEqual(new UUID(uuid)))
312                 .build());
313     }
314
315     @Override
316     @Deprecated
317     public Status deleteRow(Node node, String tableName, String uuid) {
318         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
319         Connection connection = connectionService.getConnection(node);
320         OvsdbClient client = connection.getClient();
321
322         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
323         if (parentColumn == null) {
324             parentColumn = new String[]{null, null};
325         }
326
327         logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
328                 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
329
330         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
331         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
332         this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
333                                       parentColumn[0], parentColumn[1], uuid, transactionBuilder);
334
335         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
336         List<OperationResult> operationResults;
337         try {
338             operationResults = results.get();
339             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
340                 return new StatusWithUuid(StatusCode.INTERNALERROR);
341             }
342             for (OperationResult result : operationResults) {
343                 if (result.getError() != null) {
344                     return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
345                 }
346             }
347         } catch (InterruptedException | ExecutionException e) {
348             // TODO Auto-generated catch block
349             e.printStackTrace();
350         }
351
352         return new Status(StatusCode.SUCCESS);
353     }
354
355     @Override
356     @Deprecated
357     public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
358         ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
359         return ovsTable;
360     }
361
362     @Override
363     @Deprecated
364     public Row getRow(Node node, String tableName, String uuid) {
365         Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
366         if (ovsTable == null) return null;
367         return ovsTable.get(uuid);
368     }
369
370     @Override
371     @Deprecated
372     public List<String> getTables(Node node) {
373         return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
374     }
375
376     private InetAddress getControllerIPAddress(Connection connection) {
377         List<InetAddress> controllers = null;
378         InetAddress controllerIP = null;
379
380         String addressString = System.getProperty("ovsdb.controller.address");
381
382         if (addressString != null) {
383             try {
384                 controllerIP = InetAddress.getByName(addressString);
385                 if (controllerIP != null) {
386                     return controllerIP;
387                 }
388             } catch (UnknownHostException e) {
389                 logger.error("Host {} is invalid", addressString);
390             }
391         }
392
393         addressString = System.getProperty("of.address");
394
395         if (addressString != null) {
396             try {
397                 controllerIP = InetAddress.getByName(addressString);
398                 if (controllerIP != null) {
399                     return controllerIP;
400                 }
401             } catch (UnknownHostException e) {
402                 logger.error("Host {} is invalid", addressString);
403             }
404         }
405
406         try {
407             controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
408             return controllerIP;
409         } catch (Exception e) {
410             logger.debug("Invalid connection provided to getControllerIPAddresses", e);
411         }
412         return controllerIP;
413     }
414
415     private short getControllerOFPort() {
416         Short defaultOpenFlowPort = 6633;
417         Short openFlowPort = defaultOpenFlowPort;
418         String portString = System.getProperty("of.listenPort");
419         if (portString != null) {
420             try {
421                 openFlowPort = Short.decode(portString).shortValue();
422             } catch (NumberFormatException e) {
423                 logger.warn("Invalid port:{}, use default({})", portString,
424                         openFlowPort);
425             }
426         }
427         return openFlowPort;
428     }
429
430     private UUID getCurrentControllerUuid(Node node, final String controllerTableName, final String target) {
431         ConcurrentMap<String, Row> rows = this.getRows(node, controllerTableName);
432
433         if (rows != null) {
434             for (Map.Entry<String, Row> entry : rows.entrySet()) {
435                 Controller currController = this.getTypedRow(node, Controller.class, entry.getValue());
436                 Column<GenericTableSchema, String> column = currController.getTargetColumn();
437                 String currTarget = column.getData();
438                 if (currTarget != null && currTarget.equalsIgnoreCase(target)) {
439                     return currController.getUuid();
440                 }
441             }
442         }
443         return null;
444     }
445
446     @Override
447     public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
448         Connection connection = this.getConnection(node);
449         if (connection == null) {
450             return false;
451         }
452
453         Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
454
455         Status updateOperationStatus = null;
456         try {
457             OvsdbSet<String> protocols = new OvsdbSet<String>();
458
459             String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
460             switch (ofVersion) {
461                 case OPENFLOW_10:
462                     protocols.add("OpenFlow10");
463                     break;
464                 case OPENFLOW_13:
465                     //fall through
466                 default:
467                     protocols.add("OpenFlow13");
468                     break;
469             }
470             bridge.setProtocols(protocols);
471             updateOperationStatus = this.updateRow(node, bridge.getSchema().getName(),
472                                                    null, bridgeUUID, bridge.getRow());
473             logger.debug("Bridge {} updated to {} with Status {}", bridgeUUID,
474                          protocols.toArray()[0],updateOperationStatus);
475
476         } catch (SchemaVersionMismatchException e){
477             logger.debug(e.toString());
478         }
479
480         // If we fail to update the protocols
481         if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
482             return updateOperationStatus.isSuccess();
483         }
484
485         Status status = null;
486         UUID currControllerUuid = null;
487         InetAddress ofControllerAddr = this.getControllerIPAddress(connection);
488         short ofControllerPort = getControllerOFPort();
489         String newControllerTarget = "tcp:"+ofControllerAddr.getHostAddress()+":"+ofControllerPort;
490         Controller newController = connection.getClient().createTypedRowWrapper(Controller.class);
491         newController.setTarget(newControllerTarget);
492         final String controllerTableName = newController.getSchema().getName();
493
494         currControllerUuid = getCurrentControllerUuid(node, controllerTableName, newControllerTarget);
495
496         if (currControllerUuid != null) {
497             bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
498             bridge.setController(Sets.newHashSet(currControllerUuid));
499             status = this.updateRow(node, bridge.getSchema().getName(), null, bridgeUUID, bridge.getRow());
500         } else {
501             status = this.insertRow(node, controllerTableName, bridgeUUID, newController.getRow());
502         }
503
504         if (status != null) {
505             return status.isSuccess();
506         }
507
508         return false;
509     }
510
511
512     public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
513         if (connectionService == null) {
514             logger.error("Couldn't refer to the ConnectionService");
515             return false;
516         }
517
518         try{
519             Connection connection = connectionService.getConnection(node);
520             Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
521
522             Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
523             for (String uuid : brTableCache.keySet()) {
524                 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
525                 if (bridge.getName().contains(bridgeIdentifier)) {
526                     return setOFController(node, uuid);
527                 }
528             }
529         } catch(Exception e) {
530             logger.error("Error in setBridgeOFController()",e);
531         }
532         return false;
533     }
534
535     @Override
536     public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
537         Connection connection = connectionService.getConnection(node);
538         if (connection == null) return null;
539         OvsdbClient client = connection.getClient();
540         TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
541         if (typedTable == null) return null;
542         return typedTable.getSchema().getName();
543     }
544
545     @Override
546     public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
547         Connection connection = connectionService.getConnection(node);
548         if (connection == null) return null;
549         OvsdbClient client = connection.getClient();
550         return (T)client.getTypedRowWrapper(typedClass, row);
551     }
552
553     @Override
554     public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
555         Connection connection = connectionService.getConnection(node);
556         if (connection == null) return null;
557         OvsdbClient client = connection.getClient();
558         return client.createTypedRowWrapper(typedClass);
559     }
560
561     public void _ovsconnect (CommandInterpreter ci) {
562         String bridgeName = ci.nextArgument();
563         if (bridgeName == null) {
564             ci.println("Please enter Bridge Name");
565             return;
566         }
567
568         String ovsdbserver = ci.nextArgument();
569         if (ovsdbserver == null) {
570             ci.println("Please enter valid IP-Address");
571             return;
572         }
573         try {
574             InetAddress.getByName(ovsdbserver);
575         }  catch (UnknownHostException e) {
576             logger.error("Unable to resolve " + ovsdbserver, e);
577             ci.println("Please enter valid IP-Address");
578             return;
579         }
580         String port = ci.nextArgument();
581         if (port == null) {
582             port = "6634";
583         }
584
585         ci.println("connecting to ovsdb server : "+ovsdbserver+":"+port+" ... ");
586         Map<ConnectionConstants, String> params = new HashMap<ConnectionConstants, String>();
587         params.put(ConnectionConstants.ADDRESS, ovsdbserver);
588         params.put(ConnectionConstants.PORT, port);
589         Node node = connectionService.connect(bridgeName, params);
590         if (node != null) ci.println("Node Name: "+node.toString());
591         else ci.println("Could not connect to Node");
592     }
593
594     public void _addBridge (CommandInterpreter ci) {
595         String nodeName = ci.nextArgument();
596         if (nodeName == null) {
597             ci.println("Please enter Node Name");
598             return;
599         }
600         String bridgeName = ci.nextArgument();
601         if (bridgeName == null) {
602             ci.println("Please enter Bridge Name");
603             return;
604         }
605         Status status;
606
607         Node node = Node.fromString(nodeName);
608         if (node == null) {
609             ci.println("Invalid Node");
610             return;
611         }
612         status = this.createBridgeDomain(node, bridgeName, null);
613         ci.println("Bridge creation status : "+status.toString());
614     }
615
616     public void _getBridgeDomains (CommandInterpreter ci) {
617         String nodeName = ci.nextArgument();
618         if (nodeName == null) {
619             ci.println("Please enter Node Name");
620             return;
621         }
622
623         List<String> brlist = new ArrayList<String>();
624         Node node = Node.fromString(nodeName);
625         brlist = this.getBridgeDomains(node);
626         if (node == null) {
627             ci.println("Invalid Node");
628             return;
629         }
630         ci.println("Existing Bridges: "+brlist.toString());
631     }
632
633     public void _deleteBridgeDomain (CommandInterpreter ci) {
634         String nodeName = ci.nextArgument();
635         if (nodeName == null) {
636             ci.println("Please enter Node Name");
637             return;
638         }
639         String bridgeName = ci.nextArgument();
640         if (bridgeName == null) {
641             ci.println("Please enter Bridge Name");
642             return;
643         }
644         Status status;
645         Node node = Node.fromString(nodeName);
646         if (node == null) {
647             ci.println("Invalid Node");
648             return;
649         }
650         status = this.deleteBridgeDomain(node, bridgeName);
651         ci.println("Bridge deletion status : "+status.toString());
652     }
653
654     public void _addPort (CommandInterpreter ci) {
655         String nodeName = ci.nextArgument();
656         if (nodeName == null) {
657             ci.println("Please enter Node Name");
658             return;
659         }
660
661         String bridgeName = ci.nextArgument();
662         if (bridgeName == null) {
663             ci.println("Please enter Bridge Name");
664             return;
665         }
666
667         String portName = ci.nextArgument();
668         if (portName == null) {
669             ci.println("Please enter Port Name");
670             return;
671         }
672
673         String type = ci.nextArgument();
674
675         Map<String, String> configs = new HashMap<String, String>();
676         while(true) {
677             String configKey = ci.nextArgument();
678             if (configKey == null) break;
679             String configValue = ci.nextArgument();
680             if (configValue == null) break;
681             configs.put(configKey, configValue);
682         }
683
684         Map<ConfigConstants, Object> customConfigs = null;
685         if (type != null) {
686             customConfigs = new HashMap<ConfigConstants, Object>();
687             customConfigs.put(ConfigConstants.TYPE, type);
688         }
689
690         if (configs.size() > 0) {
691             if (customConfigs == null) customConfigs = new HashMap<ConfigConstants, Object>();
692             customConfigs.put(ConfigConstants.CUSTOM, configs);
693             ci.println(customConfigs.toString());
694         }
695         Status status;
696         Node node = Node.fromString(nodeName);
697         if (node == null) {
698             ci.println("Invalid Node");
699             return;
700         }
701         status = this.addPort(node, bridgeName, portName, customConfigs);
702         ci.println("Port creation status : "+status.toString());
703     }
704
705     public void _deletePort (CommandInterpreter ci) {
706         String nodeName = ci.nextArgument();
707         if (nodeName == null) {
708             ci.println("Please enter Node Name");
709             return;
710         }
711
712         String bridgeName = ci.nextArgument();
713         if (bridgeName == null) {
714             ci.println("Please enter Bridge Name");
715             return;
716         }
717
718         String portName = ci.nextArgument();
719         if (portName == null) {
720             ci.println("Please enter Port Name");
721             return;
722         }
723
724         Status status;
725         Node node = Node.fromString(nodeName);
726         if (node == null) {
727             ci.println("Invalid Node");
728             return;
729         }
730         status = this.deletePort(node, bridgeName, portName);
731         ci.println("Port deletion status : "+status.toString());
732     }
733
734     public void _addPortVlan (CommandInterpreter ci) {
735         String nodeName = ci.nextArgument();
736         if (nodeName == null) {
737             ci.println("Please enter Node Name");
738             return;
739         }
740
741         String bridgeName = ci.nextArgument();
742         if (bridgeName == null) {
743             ci.println("Please enter Bridge Name");
744             return;
745         }
746
747         String portName = ci.nextArgument();
748         if (portName == null) {
749             ci.println("Please enter Port Name");
750             return;
751         }
752
753         String vlan = ci.nextArgument();
754         if (vlan == null) {
755             ci.println("Please enter Valid Vlan");
756             return;
757         } else {
758             try {
759             Integer.parseInt(vlan);
760             } catch (NumberFormatException e) {
761                 ci.println("Please enter Valid Vlan");
762                 return;
763             }
764         }
765
766         Map<ConfigConstants, Object> configs = new HashMap<ConfigConstants, Object>();
767         configs.put(ConfigConstants.TYPE, "VLAN");
768         configs.put(ConfigConstants.VLAN, vlan);
769
770         Status status;
771         Node node = Node.fromString(nodeName);
772         if (node == null) {
773             ci.println("Invalid Node");
774             return;
775         }
776         status = this.addPort(node, bridgeName, portName, configs);
777         ci.println("Port creation status : "+status.toString());
778     }
779
780     public void _addTunnel (CommandInterpreter ci) {
781         String nodeName = ci.nextArgument();
782         if (nodeName == null) {
783             ci.println("Please enter Node Name");
784             return;
785         }
786
787         String bridgeName = ci.nextArgument();
788         if (bridgeName == null) {
789             ci.println("Please enter Bridge Name");
790             return;
791         }
792
793         String portName = ci.nextArgument();
794         if (portName == null) {
795             ci.println("Please enter Port Name");
796             return;
797         }
798
799         String tunnelType = ci.nextArgument();
800         if (tunnelType == null) {
801             ci.println("Please enter Tunnel Type");
802             return;
803         }
804
805         String remoteIp = ci.nextArgument();
806         if (remoteIp == null) {
807             ci.println("Please enter valid Remote IP Address");
808             return;
809         }
810
811         try {
812             InetAddress.getByName(remoteIp);
813         }  catch (Exception e) {
814             logger.error("Unable to resolve " + remoteIp, e);
815             ci.println("Please enter valid Remote IP Address");
816             return;
817         }
818
819         Map<ConfigConstants, Object> configs = new HashMap<ConfigConstants, Object>();
820         configs.put(ConfigConstants.TYPE, "TUNNEL");
821         configs.put(ConfigConstants.TUNNEL_TYPE, tunnelType);
822         configs.put(ConfigConstants.DEST_IP, remoteIp);
823
824         Status status;
825         Node node = Node.fromString(nodeName);
826         if (node == null) {
827             ci.println("Invalid Node");
828             return;
829         }
830         status = this.addPort(node, bridgeName, portName, configs);
831         ci.println("Port creation status : "+status.toString());
832     }
833
834     public void _printCache (CommandInterpreter ci) {
835         String nodeName = ci.nextArgument();
836         if (nodeName == null) {
837             ci.println("Please enter Node Name");
838             return;
839         }
840         Node node = Node.fromString(nodeName);
841         if (node == null) {
842             ci.println("Invalid Node");
843             return;
844         }
845         ovsdbInventoryService.printCache(node);
846     }
847
848     public void _forceConnect (CommandInterpreter ci) {
849         String force = ci.nextArgument();
850         if (force.equalsIgnoreCase("YES")) {
851             forceConnect = true;
852         }
853         else if (force.equalsIgnoreCase("NO")) {
854             forceConnect = false;
855         }
856         else {
857             ci.println("Please enter YES or NO.");
858         }
859         ci.println("Current ForceConnect State : "+forceConnect);
860     }
861
862     @Override
863     public String getHelp() {
864         StringBuilder help = new StringBuilder();
865         help.append("---OVSDB CLI---\n");
866         help.append("\t ovsconnect <ConnectionName> <ip-address>                        - Connect to OVSDB\n");
867         help.append("\t addBridge <Node> <BridgeName>                                   - Add Bridge\n");
868         help.append("\t getBridgeDomains <Node>                                         - Get Bridges\n");
869         help.append("\t deleteBridgeDomain <Node> <BridgeName>                          - Delete a Bridge\n");
870         help.append("\t addPort <Node> <BridgeName> <PortName> <type> <options pairs>   - Add Port\n");
871         help.append("\t deletePort <Node> <BridgeName> <PortName>                       - Delete Port\n");
872         help.append("\t addPortVlan <Node> <BridgeName> <PortName> <vlan>               - Add Port, Vlan\n");
873         help.append("\t addTunnel <Node> <Bridge> <Port> <tunnel-type> <remote-ip>      - Add Tunnel\n");
874         help.append("\t printCache <Node>                                               - Prints Table Cache");
875         return help.toString();
876     }
877
878
879     /**
880      * Add a new bridge
881      * @param node Node serving this configuration service
882      * @param bridgeIdentifier String representation of a Bridge Connector
883      * @return Bridge Connector configurations
884      */
885     @Override
886     @Deprecated
887     public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> configs) {
888         Connection connection = connectionService.getConnection(node);
889         OvsdbClient client = connection.getClient();
890         Bridge bridge = client.createTypedRowWrapper(Bridge.class);
891         bridge.setName(bridgeIdentifier);
892
893         String ovsTableUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
894         return this.insertRow(node, bridge.getSchema().getName(), ovsTableUuid, bridge.getRow());
895     }
896
897     /**
898      * Create a Port Attached to a Bridge
899      * Ex. ovs-vsctl add-port br0 vif0
900      * @param node Node serving this configuration service
901      * @param bridgeIdentifier String representation of a Bridge Domain
902      * @param portIdentifier String representation of a user defined Port Name
903      */
904     @Override
905     @Deprecated
906     public Status addPort(Node node, String bridgeIdentifier, String portIdentifier,
907                           Map<ConfigConstants, Object> configs) {
908         Connection connection = connectionService.getConnection(node);
909         OvsdbClient client = connection.getClient();
910
911         Bridge bridge = client.getTypedRowWrapper(Bridge.class, null);
912         ConcurrentMap<String, Row> rows = this.getRows(node, bridge.getSchema().getName());
913         if (rows == null || rows.size() == 0) {
914             return new Status(StatusCode.NOTFOUND);
915         }
916         for (String bridgeUuid : rows.keySet()) {
917             Row bridgeRow = rows.get(bridgeUuid);
918             bridge = client.getTypedRowWrapper(Bridge.class, bridgeRow);
919             if (bridge.getName().equals(bridgeIdentifier)) break;
920         }
921         if (bridge.getName() == null || !bridge.getName().equals(bridgeIdentifier)) {
922             return new Status(StatusCode.NOTFOUND);
923         }
924
925         Map<String, String> options = null;
926         String type = null;
927         Set<Long> tags = null;
928         if (configs != null) {
929             type = (String) configs.get(ConfigConstants.TYPE);
930             Map<String, String> customConfigs = (Map<String, String>) configs.get(ConfigConstants.CUSTOM);
931             if (customConfigs != null) {
932                 options = new HashMap<String, String>();
933                 for (String customConfig : customConfigs.keySet()) {
934                     options.put(customConfig, customConfigs.get(customConfig));
935                 }
936             }
937         }
938
939         if (type != null) {
940             logger.debug("Port type : " + type);
941             if (type.equalsIgnoreCase(OvsVswitchdSchemaConstants.PortType.VLAN.name())) {
942                 tags = new HashSet<Long>();
943                 tags.add(Long.parseLong((String)configs.get(ConfigConstants.VLAN)));
944             }
945         }
946
947         Port port = client.createTypedRowWrapper(Port.class);
948         port.setName(portIdentifier);
949         if (tags != null) port.setTag(tags);
950         StatusWithUuid portStatus = this.insertRow(node, port.getSchema().getName(), bridge.getUuid().toString(), port.getRow());
951
952         if (!portStatus.isSuccess()) return portStatus;
953         // Ugly hack by adding a sleep for the Monitor Update to catch up.
954         // TODO : Remove this once the Select operation is in place.
955         // We are currently relying on the local Cache for any GET operation and that might fail if we try to
956         // fetch the last installed entry. Hence we need the Select operation to work.
957
958         try {
959             Thread.sleep(2000);
960         } catch (InterruptedException e) {
961             // TODO Auto-generated catch block
962             e.printStackTrace();
963         }
964
965         Interface interfaceRow = client.createTypedRowWrapper(Interface.class);
966         ConcurrentMap<String, Row> intfRows = this.getRows(node, interfaceRow.getSchema().getName());
967         if (intfRows == null || intfRows.size() == 0) {
968             return new Status(StatusCode.NOTFOUND);
969         }
970         for (String intfUuid : intfRows.keySet()) {
971             Row intfRow = intfRows.get(intfUuid);
972             interfaceRow = client.getTypedRowWrapper(Interface.class, intfRow);
973             if (interfaceRow == null || interfaceRow.getName() == null) continue;
974             if (interfaceRow.getName().equals(portIdentifier)) break;
975         }
976         if (interfaceRow.getName() == null || !interfaceRow.getName().equals(portIdentifier)) {
977             return new Status(StatusCode.NOTFOUND);
978         }
979         Interface updateInterface = client.createTypedRowWrapper(Interface.class);
980         if (type != null) {
981             logger.debug("Interface type : " + type);
982             if (type.equalsIgnoreCase(OvsVswitchdSchemaConstants.PortType.TUNNEL.name())) {
983                 updateInterface.setType((String)configs.get(ConfigConstants.TUNNEL_TYPE));
984                 if (options == null) options = new HashMap<String, String>();
985                 options.put("remote_ip", (String)configs.get(ConfigConstants.DEST_IP));
986             } else if (type.equalsIgnoreCase(OvsVswitchdSchemaConstants.PortType.PATCH.name()) ||
987                        type.equalsIgnoreCase(OvsVswitchdSchemaConstants.PortType.INTERNAL.name())) {
988                 updateInterface.setType(type.toLowerCase());
989             }
990         }
991         if (options != null) {
992             updateInterface.setOptions(options);
993         }
994
995         Status intfStatus = null;
996         intfStatus = this.updateRow(node, interfaceRow.getSchema().getName(), portStatus.getUuid().toString(),
997                                     interfaceRow.getUuid().toString(), updateInterface.getRow());
998
999         if (intfStatus.isSuccess()) return portStatus;
1000         return intfStatus;
1001     }
1002
1003     /**
1004      * Implements the OVS Connection for Managers
1005      *
1006      * @param node Node serving this configuration service
1007      * @param managerip String Representing IP and connection types
1008      */
1009     @SuppressWarnings("unchecked")
1010     @Deprecated
1011     public boolean setManager(Node node, String managerip) {
1012         Connection connection = connectionService.getConnection(node);
1013         OvsdbClient client = connection.getClient();
1014         Manager manager = client.createTypedRowWrapper(Manager.class);
1015         manager.setTarget(ImmutableSet.of(managerip));
1016
1017         OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
1018         ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
1019         if (row == null || row.size() == 0) {
1020             return false;
1021         }
1022         String ovsTableUuid = (String)row.keySet().toArray()[0];
1023
1024         Status status = this.insertRow(node, manager.getSchema().getName(), ovsTableUuid, manager.getRow());
1025         return status.isSuccess();
1026     }
1027
1028     @Override
1029     @Deprecated
1030     public Status addBridgeDomainConfig(Node node, String bridgeIdentfier,
1031             Map<ConfigConstants, Object> configs) {
1032         String mgmt = (String)configs.get(ConfigConstants.MGMT);
1033         if (mgmt != null) {
1034             if (setManager(node, mgmt)) return new Status(StatusCode.SUCCESS);
1035         }
1036         return new Status(StatusCode.BADREQUEST);
1037     }
1038
1039     @Override
1040     @Deprecated
1041     public Status deletePort(Node node, String bridgeIdentifier, String portIdentifier) {
1042         Connection connection = connectionService.getConnection(node);
1043         OvsdbClient client = connection.getClient();
1044
1045         Port port = client.getTypedRowWrapper(Port.class, null);
1046         ConcurrentMap<String, Row> rows = this.getRows(node, port.getSchema().getName());
1047         if (rows == null || rows.size() == 0) {
1048             return new Status(StatusCode.NOTFOUND);
1049         }
1050         for (String portUuid : rows.keySet()) {
1051             Row portRow = rows.get(portUuid);
1052             port = client.getTypedRowWrapper(Port.class, portRow);
1053             if (port.getName().equals(portIdentifier)) break;
1054         }
1055         if (port.getName() == null || !port.getName().equals(portIdentifier)) {
1056             return new Status(StatusCode.NOTFOUND);
1057         }
1058         return this.deleteRow(node, port.getSchema().getName(), port.getUuid().toString());
1059     }
1060
1061     @Override
1062     @Deprecated
1063     public Status deleteBridgeDomain(Node node, String bridgeIdentifier) {
1064         Connection connection = connectionService.getConnection(node);
1065         OvsdbClient client = connection.getClient();
1066
1067         Bridge bridge = client.getTypedRowWrapper(Bridge.class, null);
1068         ConcurrentMap<String, Row> rows = this.getRows(node, bridge.getSchema().getName());
1069         if (rows == null || rows.size() == 0) {
1070             return new Status(StatusCode.NOTFOUND);
1071         }
1072         for (String bridgeUuid : rows.keySet()) {
1073             Row bridgeRow = rows.get(bridgeUuid);
1074             bridge = client.getTypedRowWrapper(Bridge.class, bridgeRow);
1075             if (bridge.getName().equals(bridgeIdentifier)) break;
1076         }
1077         if (bridge.getName() == null || !bridge.getName().equals(bridgeIdentifier)) {
1078             return new Status(StatusCode.NOTFOUND);
1079         }
1080         return this.deleteRow(node, bridge.getSchema().getName(), bridge.getUuid().toString());
1081     }
1082
1083     @Override
1084     public List<String> getBridgeDomains(Node node) {
1085         if (connectionService == null) {
1086             logger.error("Couldn't refer to the ConnectionService");
1087             return null;
1088         }
1089
1090         Connection connection = connectionService.getConnection(node);
1091         Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
1092         List<String> brlist = new ArrayList<String>();
1093         Map<String, Row> brTableCache = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
1094         if(brTableCache != null){
1095             for (String uuid : brTableCache.keySet()) {
1096                 bridge = connection.getClient().getTypedRowWrapper(Bridge.class, brTableCache.get(uuid));
1097                 brlist.add(bridge.getName());
1098             }
1099         }
1100         return brlist;
1101     }
1102
1103     @Override
1104     public NodeConnector getNodeConnector(Node arg0, String arg1, String arg2) {
1105         return null;
1106     }
1107
1108     @Override
1109     @Deprecated
1110     public Status addPortConfig(Node node, String bridgeIdentifier, String portIdentifier,
1111             Map<ConfigConstants, Object> configs) {
1112         // TODO Auto-generated method stub
1113         return null;
1114     }
1115
1116     @Override
1117     @Deprecated
1118     public Node getBridgeDomainNode(Node node, String bridgeIdentifier) {
1119         // TODO Auto-generated method stub
1120         return null;
1121     }
1122
1123     @Override
1124     @Deprecated
1125     public Map<ConfigConstants, Object> getPortConfigs(Node node, String bridgeIdentifier,
1126             String portIdentifier) {
1127         // TODO Auto-generated method stub
1128         return null;
1129     }
1130
1131     @Override
1132     @Deprecated
1133     public Status removeBridgeDomainConfig(Node node, String bridgeIdentifier,
1134             Map<ConfigConstants, Object> configs) {
1135         // TODO Auto-generated method stub
1136         return null;
1137     }
1138
1139     @Override
1140     @Deprecated
1141     public Status removePortConfig(Node node, String bridgeIdentifier, String portIdentifier,
1142             Map<ConfigConstants, Object> configs) {
1143         // TODO Auto-generated method stub
1144         return null;
1145     }
1146
1147     @Override
1148     @Deprecated
1149     public Map<ConfigConstants, Object> getBridgeDomainConfigs(Node node, String bridgeIdentifier) {
1150         // TODO Auto-generated method stub
1151         return null;
1152     }
1153
1154
1155     // SCHEMA-INDEPENDENT Configuration Service APIs
1156
1157     private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
1158         ConcurrentMap<String, ConcurrentMap<String, Row>> cache  = ovsdbInventoryService.getCache(node, databaseName);
1159         if (cache == null) return null;
1160         for (String tableName : cache.keySet()) {
1161             ConcurrentMap<String, Row> rows = cache.get(tableName);
1162             if (rows.get(rowUuid.toString()) != null) {
1163                 return tableName;
1164             }
1165         }
1166         return null;
1167     }
1168
1169     private String getReferencingColumn (TableSchema<?> parentTableSchema, String childTableName) throws OvsdbPluginException {
1170         Map<String, ColumnSchema> columnSchemas = parentTableSchema.getColumnSchemas();
1171         String refColumn = null;
1172         for (String columnName : columnSchemas.keySet()) {
1173             ColumnSchema columnSchema = columnSchemas.get(columnName);
1174             if (columnSchema.getType().getBaseType().getClass().equals(UuidBaseType.class)) {
1175                 UuidBaseType refType = (UuidBaseType)columnSchema.getType().getBaseType();
1176                 if (refType.getRefTable() != null && refType.getRefTable().equalsIgnoreCase(childTableName)) {
1177                     if (refColumn == null) {
1178                         refColumn = columnName;
1179                     } else {
1180                         throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
1181                     }
1182                 }
1183             }
1184         }
1185         if (refColumn != null) {
1186             return refColumn;
1187         }
1188         throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
1189     }
1190     /*
1191      * A common Insert Transaction convenience method that populates the TransactionBuilder with insert operation
1192      * for a Child Row and also mutates the parent row with the UUID of the inserted Child.
1193      */
1194     private void processInsertTransaction(OvsdbClient client, String databaseName, String childTable,
1195                                     String parentTable, UUID parentUuid, String parentColumn, String namedUuid,
1196                                     Row<GenericTableSchema> row,
1197                                     TransactionBuilder transactionBuilder) {
1198         // Insert the row as the first transaction entry
1199         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1200         TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
1201         transactionBuilder.add(op.insert(childTableSchema, row)
1202                         .withId(namedUuid));
1203
1204         // Followed by the Mutation
1205         if (parentColumn != null) {
1206             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
1207             ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
1208             ColumnSchema<GenericTableSchema, UUID> _uuid = parentTableSchema.column("_uuid", UUID.class);
1209
1210             transactionBuilder
1211                 .add(op.mutate(parentTableSchema)
1212                         .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
1213                         .where(_uuid.opEqual(parentUuid))
1214                         .build());
1215         }
1216     }
1217
1218     /**
1219      * insert a Row in a Table of a specified Database Schema.
1220      *
1221      * This method can insert just a single Row specified in the row parameter.
1222      * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
1223      * can insert a hierarchy of rows with parent-child relationship.
1224      *
1225      * @param node OVSDB Node
1226      * @param databaseName Database Name that represents the Schema supported by the node.
1227      * @param tableName Table on which the row is inserted
1228      * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
1229      * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
1230      * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
1231      * @param row Row of table Content to be inserted
1232      * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
1233      * @return UUID of the inserted Row
1234      */
1235     @Override
1236     public UUID insertRow(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
1237                           String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
1238         Connection connection = connectionService.getConnection(node);
1239         OvsdbClient client = connection.getClient();
1240         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1241         TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
1242
1243         Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
1244
1245         ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
1246         Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
1247         return uuid.getData();
1248     }
1249
1250     /**
1251      * insert a Row in a Table of a specified Database Schema. This is a convenience method on top of
1252      * {@link insertRow(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertRow}
1253      * which assumes that OVSDB schema implementation that corresponds to the databaseName will provide
1254      * the necessary service to populate the Parent Table Name and Parent Column Name.
1255      *
1256      * This method can insert just a single Row specified in the row parameter.
1257      * But {@link #insertTree(Node, String, String, UUID, Row<GenericTableSchema>) insertTree}
1258      * can insert a hierarchy of rows with parent-child relationship.
1259      *
1260      * @param node OVSDB Node
1261      * @param databaseName Database Name that represents the Schema supported by the node.
1262      * @param tableName Table on which the row is inserted
1263      * @param parentUuid UUID of the parent table to which this operation will result in attaching/mutating.
1264      * @param row Row of table Content to be inserted
1265      * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
1266      * @return UUID of the inserted Row
1267      */
1268     @Override
1269     public UUID insertRow(Node node, String databaseName, String tableName,
1270             UUID parentRowUuid, Row<GenericTableSchema> row)
1271             throws OvsdbPluginException {
1272         return this.insertRow(node, databaseName, tableName, null, parentRowUuid, null, row);
1273     }
1274
1275     /**
1276      * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
1277      *
1278      * @param node OVSDB Node
1279      * @param databaseName Database Name that represents the Schema supported by the node.
1280      * @param tableName Table on which the row is inserted
1281      * @param parentTable Name of the Parent Table to which this operation will result in attaching/mutating.
1282      * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
1283      * @param parentColumn Name of the Column in the Parent Table to be mutated with the UUID that results from the insert operation.
1284      * @param row Row Tree with parent-child relationships via column of type refTable.
1285      * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
1286      * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
1287      */
1288     @Override
1289     public Row<GenericTableSchema> insertTree(Node node, String databaseName, String tableName, String parentTable, UUID parentUuid,
1290                                               String parentColumn, Row<GenericTableSchema> row) throws OvsdbPluginException {
1291         Connection connection = connectionService.getConnection(node);
1292         OvsdbClient client = connection.getClient();
1293
1294         if (databaseName == null || tableName == null) {
1295             throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
1296         }
1297
1298         if (parentTable == null && parentUuid != null) {
1299             parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
1300         }
1301
1302         if (parentColumn == null && parentTable != null) {
1303             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1304             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
1305             parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
1306         }
1307
1308         logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
1309                      client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
1310
1311         Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows = Maps.newConcurrentMap();
1312         extractReferencedRows(node, databaseName, row, referencedRows, 0);
1313         DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
1314         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
1315
1316         String namedUuid = "Transaction_"+ tableName;
1317         this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
1318                                       parentColumn, namedUuid, row, transactionBuilder);
1319
1320         int referencedRowsInsertIndex = transactionBuilder.getOperations().size();
1321         // Insert Referenced Rows
1322         if (referencedRows != null) {
1323             for (UUID refUuid : referencedRows.keySet()) {
1324                 Map.Entry<String, Row<GenericTableSchema>> referencedRow = referencedRows.get(refUuid);
1325                 TableSchema<GenericTableSchema> refTableSchema = dbSchema.table(referencedRow.getKey(), GenericTableSchema.class);
1326                 transactionBuilder.add(op.insert(refTableSchema, referencedRow.getValue())
1327                                 .withId(refUuid.toString()));
1328             }
1329         }
1330
1331         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1332         List<OperationResult> operationResults;
1333         try {
1334             operationResults = results.get();
1335             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1336                 throw new OvsdbPluginException("Insert Operation Failed");
1337             }
1338             for (OperationResult result : operationResults) {
1339                 if (result.getError() != null) {
1340                     throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
1341                 }
1342             }
1343             return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
1344         } catch (InterruptedException | ExecutionException e) {
1345             throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
1346         }
1347     }
1348
1349     /**
1350      * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct.
1351      * This is a convenience method on top of {@link #insertTree(Node, String, String, String, UUID, String, Row<GenericTableSchema>) insertTree}
1352      *
1353      * @param node OVSDB Node
1354      * @param databaseName Database Name that represents the Schema supported by the node.
1355      * @param tableName Table on which the row is inserted
1356      * @param parentUuid UUID of a Row in parent table to which this operation will result in attaching/mutating.
1357      * @param row Row Tree with parent-child relationships via column of type refTable.
1358      * @throws OvsdbPluginException Any failure during the insert transaction will result in a specific exception.
1359      * @return Returns the row tree with the UUID of every inserted Row populated in the _uuid column of every row in the tree
1360      */
1361     @Override
1362     public Row<GenericTableSchema> insertTree(Node node, String databaseName,
1363             String tableName, UUID parentRowUuid, Row<GenericTableSchema> row)
1364             throws OvsdbPluginException {
1365         return this.insertTree(node, databaseName, tableName, null, parentRowUuid, null, row);
1366     }
1367
1368     /**
1369      * Convenience method that helps insertTree to extract Rows that are referenced directly from within a primary row
1370      * to be inserted. These referenced rows are *NOT* defined in the OVSDB specification. But, we felt that from a northbound
1371      * application standpoint, having such an option is useful and our implementation supports it for applications to make use of.
1372      * In short, whichever ColumnSchema is based on an UUID (refered by RefTable in schema), applications can directly insert an
1373      * entire row and this method will help navigate it through and identify such cases.
1374      * After identifying these Referenced Rows, it will modify the primary row with Named UUIDs and fill out the referencedRows
1375      * Map structure so that insertTree can insert all the Rows defined in this Tree of rows in a single transaction with automatic
1376      * Mutation on the parent rows.
1377      *
1378      * @param node OVSDB Node
1379      * @param dbName Database Name that represents the Schema supported by the node.
1380      * @param row Row Tree with parent-child relationships via column of type refTable.
1381      * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
1382      * @param namedUuidSuffix Named UUID must be unique for every new Row insert within a given transaction.
1383      *        This index will help to retain the uniqueness.
1384      */
1385     private void extractReferencedRows(Node node, String dbName, Row<GenericTableSchema> row,
1386                                        Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
1387                                        int namedUuidSuffix) {
1388         OvsdbClient client = connectionService.getConnection(node).getClient();
1389         Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
1390         for (Column column : columns) {
1391             if (column.getData() != null) {
1392                 if (column.getData() instanceof ReferencedRow) {
1393                     ReferencedRow refRowObject = (ReferencedRow)column.getData();
1394                     UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
1395                     column.setData(refUuid);
1396                     try {
1397                         DatabaseSchema dbSchema = client.getSchema(dbName).get();
1398                         GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
1399                         Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
1400                         referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
1401                         extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
1402                     } catch (InterruptedException | ExecutionException e) {
1403                         logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
1404                     }
1405                 } else if (column.getData() instanceof OvsdbSet) {
1406                     OvsdbSet<Object> setObject = (OvsdbSet<Object>)column.getData();
1407                     OvsdbSet<Object> modifiedSet = new OvsdbSet<Object>();
1408                     for (Object obj : setObject) {
1409                         if (obj instanceof ReferencedRow) {
1410                             ReferencedRow refRowObject = (ReferencedRow)obj;
1411                             UUID refUuid = new UUID("NamedUuid"+namedUuidSuffix++);
1412                             modifiedSet.add(refUuid);
1413                             try {
1414                                 DatabaseSchema dbSchema = client.getSchema(dbName).get();
1415                                 GenericTableSchema schema = dbSchema.table(refRowObject.getRefTable(), GenericTableSchema.class);
1416                                 Row<GenericTableSchema> refRow = schema.createRow((ObjectNode)refRowObject.getJsonNode());
1417                                 referencedRows.put(refUuid, new AbstractMap.SimpleEntry<String, Row<GenericTableSchema>>(refRowObject.getRefTable(), refRow));
1418                                 extractReferencedRows(node, dbName, refRow, referencedRows, namedUuidSuffix);
1419                             } catch (InterruptedException | ExecutionException e) {
1420                                 logger.error("Exception while extracting multi-level Row references " + e.getLocalizedMessage());
1421                             }
1422                         } else {
1423                             modifiedSet.add(obj);
1424                         }
1425                     }
1426                     column.setData(modifiedSet);
1427                 }
1428             }
1429         }
1430     }
1431
1432     /**
1433      * getNormalizedRow normalizes the Row from a namedUuid Space as defined in extractReferencedRows to the actual Uuid as created
1434      * by the Ovsdb-server. In order to perform this normalization, it processes the operation results for a corresponding Transaction
1435      * where the referenced rows are inserted along with the Primary row. It changes the named-Uuid to the actual Uuid before returning
1436      * the Row to the application.
1437      *
1438      * @param dbSchema Database Schema supported by the node.
1439      * @param row Row Tree with parent-child relationships via column of type refTable.
1440      * @param tableName Table on which the row is inserted
1441      * @param referencedRows Map of Named-UUID to the actual referenced row (with RefTable)
1442      * @param operationResults Operation Results returned by ovsdb-server for the insertTree transaction
1443      * @param referencedRowsInsertIndex Starting index in OperationResults from which the ReferencedRow insert results begin.
1444      * @return
1445      */
1446     private Row<GenericTableSchema> getNormalizedRow(DatabaseSchema dbSchema, String tableName, Row<GenericTableSchema> row,
1447                                                      Map<UUID, Map.Entry<String, Row<GenericTableSchema>>> referencedRows,
1448                                                      List<OperationResult> operationResults, int referencedRowsInsertIndex) {
1449         UUID primaryRowUuid = operationResults.get(0).getUuid();
1450         TableSchema<GenericTableSchema> primaryRowTableSchema = dbSchema.table(tableName, GenericTableSchema.class);
1451         ColumnSchema<GenericTableSchema, UUID> _uuid = primaryRowTableSchema.column("_uuid", UUID.class);
1452         if (_uuid != null) {
1453             Column<GenericTableSchema, UUID> _uuidColumn = new Column<GenericTableSchema, UUID>(_uuid, primaryRowUuid);
1454             row.addColumn("_uuid", _uuidColumn);
1455         }
1456
1457         if (referencedRows != null) {
1458             Collection<Column<GenericTableSchema, ?>> columns = row.getColumns();
1459             if (referencedRows != null) {
1460                 for (int idx=0; idx < referencedRows.keySet().size(); idx++) {
1461                     UUID refUuid = (UUID) referencedRows.keySet().toArray()[idx];
1462                     for (Column column : columns) {
1463                         if (column.getData() != null) {
1464                             if ((column.getData() instanceof UUID) && column.getData().equals(refUuid)) {
1465                                 column.setData(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
1466                             } else if ((column.getData() instanceof OvsdbSet) && ((OvsdbSet)column.getData()).contains(refUuid)) {
1467                                 OvsdbSet<UUID> refSet = (OvsdbSet<UUID>)column.getData();
1468                                 refSet.remove(refUuid);
1469                                 refSet.add(operationResults.get(referencedRowsInsertIndex + idx).getUuid());
1470                             }
1471                         }
1472                     }
1473                 }
1474             }
1475         }
1476         return row;
1477     }
1478
1479     @Override
1480     public Row<GenericTableSchema> updateRow(Node node, String databaseName,
1481             String tableName, UUID rowUuid, Row<GenericTableSchema> row,
1482             boolean overwrite) throws OvsdbPluginException {
1483         Connection connection = connectionService.getConnection(node);
1484         OvsdbClient client = connection.getClient();
1485
1486         logger.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
1487                       client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
1488         try{
1489             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1490             TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
1491             TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
1492             ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
1493             transactionBuilder.add(op.update(tableSchema, row)
1494                                      .where(_uuid.opEqual(rowUuid))
1495                                      .build());
1496
1497             ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1498             List<OperationResult> operationResults = results.get();
1499             for (OperationResult result : operationResults) {
1500                 if (result.getError() != null) {
1501                     throw new OvsdbPluginException("Error updating row : "+ result.getError());
1502                 }
1503             }
1504             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1505                 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
1506             }
1507
1508             return this.getRow(node, databaseName, tableName, rowUuid);
1509         } catch(Exception e){
1510             throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
1511         }
1512     }
1513
1514     @Override
1515     public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
1516             String parentColumn, UUID rowUuid) throws OvsdbPluginException {
1517         Connection connection = connectionService.getConnection(node);
1518         OvsdbClient client = connection.getClient();
1519
1520         if (parentTable == null && parentRowUuid != null) {
1521             parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
1522         }
1523
1524         if (parentColumn == null && parentTable != null) {
1525             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1526             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
1527             parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
1528         }
1529
1530         logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
1531                 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
1532
1533         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1534         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
1535         this.processDeleteTransaction(client, databaseName, tableName,
1536                                       parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
1537
1538         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1539         List<OperationResult> operationResults;
1540         try {
1541             operationResults = results.get();
1542             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1543                 throw new OvsdbPluginException("Delete Operation Failed");
1544             }
1545             for (OperationResult result : operationResults) {
1546                 if (result.getError() != null) {
1547                     throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
1548                 }
1549             }
1550         } catch (InterruptedException | ExecutionException e) {
1551             // TODO Auto-generated catch block
1552             e.printStackTrace();
1553         }
1554     }
1555
1556     @Override
1557     public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
1558         this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
1559     }
1560
1561     @Override
1562     public Row<GenericTableSchema> getRow(Node node, String databaseName,
1563             String tableName, UUID uuid) throws OvsdbPluginException {
1564         ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
1565         if (rows != null) {
1566             return rows.get(uuid);
1567         }
1568         return null;
1569     }
1570
1571     @Override
1572     public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
1573             String databaseName, String tableName) throws OvsdbPluginException {
1574         ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
1575         if (ovsTable == null) return null;
1576         ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
1577         for (String uuidStr : ovsTable.keySet()) {
1578             tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
1579         }
1580         return tableDB;
1581     }
1582
1583     @Override
1584     public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
1585             String databaseName, String tableName, String fiqlQuery)
1586             throws OvsdbPluginException {
1587         return this.getRows(node, databaseName, tableName);
1588     }
1589
1590     @Override
1591     public List<String> getTables(Node node, String databaseName) throws OvsdbPluginException {
1592         ConcurrentMap<String, ConcurrentMap<String, Row>> cache  = ovsdbInventoryService.getCache(node, databaseName);
1593         if (cache == null) return null;
1594         return new ArrayList<String>(cache.keySet());
1595     }
1596 }