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