Merge "Fix critical Sonar Issue: ConfigurationServiceImpl.java"
[netvirt.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.opendaylight.ovsdb.utils.config.ConfigProperties;
70
71 import org.osgi.framework.BundleContext;
72 import org.osgi.framework.FrameworkUtil;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 import com.fasterxml.jackson.databind.node.ObjectNode;
77 import com.google.common.collect.ImmutableSet;
78 import com.google.common.collect.Maps;
79 import com.google.common.collect.Sets;
80 import com.google.common.util.concurrent.ListenableFuture;
81
82 public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigService,
83                                                  OvsdbConfigurationService,
84                                                  OvsdbConfigService,
85                                                  CommandProvider
86 {
87     private static final Logger logger = LoggerFactory
88             .getLogger(ConfigurationServiceImpl.class);
89
90     OvsdbConnectionService connectionService;
91     OvsdbInventoryService ovsdbInventoryService;
92     boolean forceConnect = false;
93     protected static final String OPENFLOW_10 = "1.0";
94     protected static final String OPENFLOW_13 = "1.3";
95
96     void init() {
97     }
98
99     /**
100      * Function called by the dependency manager when at least one dependency
101      * become unsatisfied or when the component is shutting down because for
102      * example bundle is being stopped.
103      *
104      */
105     void destroy() {
106     }
107
108     /**
109      * Function called by dependency manager after "init ()" is called and after
110      * the services provided by the class are registered in the service registry
111      *
112      */
113     void start() {
114         registerWithOSGIConsole();
115     }
116
117     private void registerWithOSGIConsole() {
118         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
119                 .getBundleContext();
120         bundleContext.registerService(CommandProvider.class.getName(), this,
121                 null);
122     }
123
124     /**
125      * Function called by the dependency manager before the services exported by
126      * the component are unregistered, this will be followed by a "destroy ()"
127      * calls
128      *
129      */
130     void stop() {
131     }
132
133     public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
134         this.connectionService = connectionService;
135     }
136
137     public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
138         if (this.connectionService == connectionService) {
139             this.connectionService = null;
140         }
141     }
142
143     public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
144         this.ovsdbInventoryService = ovsdbInventoryService;
145     }
146
147     public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
148         if (this.ovsdbInventoryService == ovsdbInventoryService) {
149             this.ovsdbInventoryService = null;
150         }
151     }
152
153     private IClusterGlobalServices clusterServices;
154
155     public void setClusterServices(IClusterGlobalServices i) {
156         this.clusterServices = i;
157     }
158
159     public void unsetClusterServices(IClusterGlobalServices i) {
160         if (this.clusterServices == i) {
161             this.clusterServices = null;
162         }
163     }
164
165     private Connection getConnection (Node node) {
166         Connection connection = connectionService.getConnection(node);
167         if (connection == null || !connection.getClient().isActive()) {
168             return null;
169         }
170
171         return connection;
172     }
173     /*
174      * There are a few Open_vSwitch schema specific special case handling to be done for
175      * the older API (such as by inserting a mandatory Interface row automatically upon inserting
176      * a Port row.
177      */
178     private void handleSpecialInsertCase(OvsdbClient client, String databaseName,
179             String tableName, String uuid, Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
180         Port port = client.getTypedRowWrapper(Port.class, null);
181         if (databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME) && tableName.equals(port.getSchema().getName())) {
182             port = client.getTypedRowWrapper(Port.class, row);
183             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
184             TableSchema<GenericTableSchema> tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
185             ColumnSchema<GenericTableSchema, Set<UUID>> columnSchema = tableSchema.multiValuedColumn("interfaces", UUID.class);
186             String namedUuid = "Special_"+tableName;
187             List<Operation> priorOperations = transactionBuilder.getOperations();
188             Insert portOperation = (Insert)priorOperations.get(0);
189             portOperation.value(columnSchema, new UUID(namedUuid));
190
191             Column<GenericTableSchema, ?> nameColumn = port.getNameColumn();
192             List<Column<GenericTableSchema, ?>> columns = new ArrayList<Column<GenericTableSchema, ?>>();
193             columns.add(nameColumn);
194             Row<GenericTableSchema> intfRow = new Row<GenericTableSchema>(tableSchema, columns);
195             this.processTypedInsertTransaction(client, databaseName, "Interface", null, null, null, namedUuid, intfRow, transactionBuilder);
196         }
197     }
198
199     /*
200      * A common Transaction that takes in old API style Parent_uuid and inserts a mutation on
201      * the parent table for the newly inserted Child.
202      * Due to some additional special case(s), the Transaction is further amended by handleSpecialInsertCase
203      */
204     private void processTypedInsertTransaction(OvsdbClient client, String databaseName, String childTable,
205                                     String parentTable, String parentUuid, String parentColumn, String namedUuid,
206                                     Row<GenericTableSchema> row, TransactionBuilder transactionBuilder) {
207         this.processInsertTransaction(client, databaseName, childTable, parentTable, new UUID(parentUuid), parentColumn,
208                                       namedUuid, row, transactionBuilder);
209         /*
210          * There are a few Open_vSwitch schema specific special case handling to be done for
211          * the older API (such as by inserting a mandatory Interface row automatically upon inserting
212          * a Port row.
213          */
214         handleSpecialInsertCase(client, databaseName, childTable, namedUuid, row, transactionBuilder);
215     }
216
217     /*
218      * TODO : Move all the Special Cases out of ConfigurationService and into the Schema specific bundles.
219      * But that makes plugin more reliant on the Typed Bundles more than just API wrapper.
220      * Keeping these Special Handling locally till we introduce the full schema independent APIs in the
221      * plugin layer.
222      */
223     public String getSpecialCaseParentUUID(Node node, String databaseName, String childTableName) {
224         if (!databaseName.equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) return null;
225         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(childTableName);
226         if (parentColumn != null && parentColumn[0].equals(OvsVswitchdSchemaConstants.DATABASE_NAME)) {
227             Connection connection = connectionService.getConnection(node);
228             OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
229             ConcurrentMap<String, Row> row = this.getRows(node, openVSwitch.getSchema().getName());
230             if (row == null || row.size() == 0) return null;
231             return (String)row.keySet().toArray()[0];
232         }
233         return null;
234     }
235
236     /*
237      * Though this is a New API that takes in Row object, this still is considered a
238      * Deprecated call because of the assumption with a Single Row insertion.
239      * An ideal insertRow must be able to take in multiple Rows, which includes the
240      * Row being inserted in one Table and other Rows that needs mutate in other Tables.
241      */
242     @Override
243     @Deprecated
244     public StatusWithUuid insertRow(Node node, String tableName, String parentUuid, Row<GenericTableSchema> row) {
245         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
246         if (parentColumn == null) {
247             parentColumn = new String[]{null, null};
248         }
249
250         Connection connection = connectionService.getConnection(node);
251         OvsdbClient client = connection.getClient();
252
253         if (parentUuid == null) {
254             parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
255         }
256         logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
257                      client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
258
259         DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
260         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
261
262         String namedUuid = "Transaction_"+ tableName;
263         this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
264                                 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
265                                 row, transactionBuilder);
266
267         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
268         List<OperationResult> operationResults;
269         try {
270             operationResults = results.get();
271             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
272                 return new StatusWithUuid(StatusCode.INTERNALERROR);
273             }
274             for (OperationResult result : operationResults) {
275                 if (result.getError() != null) {
276                     return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
277                 }
278             }
279             UUID uuid = operationResults.get(0).getUuid();
280             return new StatusWithUuid(StatusCode.SUCCESS, uuid);
281         } catch (InterruptedException | ExecutionException e) {
282             // TODO Auto-generated catch block
283             return new StatusWithUuid(StatusCode.INTERNALERROR, e.getLocalizedMessage());
284         }
285
286     }
287
288     @Override
289     @Deprecated
290     public Status updateRow (Node node, String tableName, String parentUUID, String rowUUID, Row row) {
291         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
292         Row<GenericTableSchema> updatedRow = this.updateRow(node, databaseName, tableName, new UUID(rowUUID), row, true);
293         return new StatusWithUuid(StatusCode.SUCCESS);
294     }
295
296     private void processDeleteTransaction(OvsdbClient client, String databaseName, String childTable,
297                                     String parentTable, String parentColumn, String uuid, TransactionBuilder transactionBuilder) {
298         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
299         TableSchema<GenericTableSchema> childTableSchema = dbSchema.table(childTable, GenericTableSchema.class);
300
301         if (parentColumn != null) {
302             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
303             ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
304             transactionBuilder
305                 .add(op.mutate(parentTableSchema)
306                         .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
307                         .where(parentColumnSchema.opIncludes(new UUID(uuid)))
308                         .build());
309         }
310
311         ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
312         transactionBuilder.add(op.delete(childTableSchema)
313                 .where(_uuid.opEqual(new UUID(uuid)))
314                 .build());
315     }
316
317     @Override
318     @Deprecated
319     public Status deleteRow(Node node, String tableName, String uuid) {
320         String databaseName = OvsVswitchdSchemaConstants.DATABASE_NAME;
321         Connection connection = connectionService.getConnection(node);
322         OvsdbClient client = connection.getClient();
323
324         String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
325         if (parentColumn == null) {
326             parentColumn = new String[]{null, null};
327         }
328
329         logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
330                 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
331
332         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
333         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
334         this.processDeleteTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
335                                       parentColumn[0], parentColumn[1], uuid, transactionBuilder);
336
337         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
338         List<OperationResult> operationResults;
339         try {
340             operationResults = results.get();
341             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
342                 return new StatusWithUuid(StatusCode.INTERNALERROR);
343             }
344             for (OperationResult result : operationResults) {
345                 if (result.getError() != null) {
346                     return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
347                 }
348             }
349         } catch (InterruptedException | ExecutionException e) {
350             logger.error("Error in deleteRow()", e);
351         }
352
353         return new Status(StatusCode.SUCCESS);
354     }
355
356     @Override
357     @Deprecated
358     public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
359         ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
360         return ovsTable;
361     }
362
363     @Override
364     @Deprecated
365     public Row getRow(Node node, String tableName, String uuid) {
366         Map<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME,  tableName);
367         if (ovsTable == null) return null;
368         return ovsTable.get(uuid);
369     }
370
371     @Override
372     @Deprecated
373     public List<String> getTables(Node node) {
374         return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
375     }
376
377     private InetAddress getControllerIPAddress(Connection connection) {
378         InetAddress controllerIP = null;
379
380         String addressString = ConfigProperties.getProperty(this.getClass(), "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 = ConfigProperties.getProperty(this.getClass(), "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 = ConfigProperties.getProperty(this.getClass(), "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             logger.error("Thread interrupted", e);
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                                                    " Details: " + result.getDetails());
1503                 }
1504             }
1505             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1506                 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
1507             }
1508
1509             return this.getRow(node, databaseName, tableName, rowUuid);
1510         } catch(Exception e){
1511             throw new OvsdbPluginException("Error updating row due to an exception "+ e.getMessage());
1512         }
1513     }
1514
1515     @Override
1516     public void deleteRow(Node node, String databaseName, String tableName, String parentTable, UUID parentRowUuid,
1517             String parentColumn, UUID rowUuid) throws OvsdbPluginException {
1518         Connection connection = connectionService.getConnection(node);
1519         OvsdbClient client = connection.getClient();
1520
1521         if (parentTable == null && parentRowUuid != null) {
1522             parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
1523         }
1524
1525         if (parentColumn == null && parentTable != null) {
1526             DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1527             TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
1528             parentColumn = this.getReferencingColumn(parentTableSchema, tableName);
1529         }
1530
1531         logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
1532                 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
1533
1534         DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1535         TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
1536         this.processDeleteTransaction(client, databaseName, tableName,
1537                                       parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
1538
1539         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1540         List<OperationResult> operationResults;
1541         try {
1542             operationResults = results.get();
1543             if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1544                 throw new OvsdbPluginException("Delete Operation Failed");
1545             }
1546             for (OperationResult result : operationResults) {
1547                 if (result.getError() != null) {
1548                     throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
1549                 }
1550             }
1551         } catch (InterruptedException | ExecutionException e) {
1552             // TODO Auto-generated catch block
1553             e.printStackTrace();
1554         }
1555     }
1556
1557     @Override
1558     public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
1559         this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
1560     }
1561
1562     @Override
1563     public Row<GenericTableSchema> getRow(Node node, String databaseName,
1564             String tableName, UUID uuid) throws OvsdbPluginException {
1565         ConcurrentMap<UUID, Row<GenericTableSchema>> rows = this.getRows(node, databaseName, tableName);
1566         if (rows != null) {
1567             return rows.get(uuid);
1568         }
1569         return null;
1570     }
1571
1572     @Override
1573     public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
1574             String databaseName, String tableName) throws OvsdbPluginException {
1575         ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, databaseName, tableName);
1576         if (ovsTable == null) return null;
1577         ConcurrentMap<UUID, Row<GenericTableSchema>> tableDB = Maps.newConcurrentMap();
1578         for (String uuidStr : ovsTable.keySet()) {
1579             tableDB.put(new UUID(uuidStr), ovsTable.get(uuidStr));
1580         }
1581         return tableDB;
1582     }
1583
1584     @Override
1585     public ConcurrentMap<UUID, Row<GenericTableSchema>> getRows(Node node,
1586             String databaseName, String tableName, String fiqlQuery)
1587             throws OvsdbPluginException {
1588         return this.getRows(node, databaseName, tableName);
1589     }
1590
1591     @Override
1592     public List<String> getTables(Node node, String databaseName) throws OvsdbPluginException {
1593         ConcurrentMap<String, ConcurrentMap<String, Row>> cache  = ovsdbInventoryService.getCache(node, databaseName);
1594         if (cache == null) return null;
1595         return new ArrayList<String>(cache.keySet());
1596     }
1597 }