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