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