2 * Copyright (C) 2013 Red Hat, Inc.
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
8 * Authors : Madhu Venugopal, Brent Salisbury, Keith Burns
10 package org.opendaylight.ovsdb.plugin.impl;
12 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
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;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ExecutionException;
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;
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;
79 public class ConfigurationServiceImpl implements IPluginInBridgeDomainConfigService,
80 OvsdbConfigurationService,
84 private static final Logger logger = LoggerFactory
85 .getLogger(ConfigurationServiceImpl.class);
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";
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.
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
111 registerWithOSGIConsole();
114 private void registerWithOSGIConsole() {
115 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
117 bundleContext.registerService(CommandProvider.class.getName(), this,
122 * Function called by the dependency manager before the services exported by
123 * the component are unregistered, this will be followed by a "destroy ()"
130 public void setConnectionServiceInternal(OvsdbConnectionService connectionService) {
131 this.connectionService = connectionService;
134 public void unsetConnectionServiceInternal(OvsdbConnectionService connectionService) {
135 if (this.connectionService == connectionService) {
136 this.connectionService = null;
140 public void setOvsdbInventoryService(OvsdbInventoryService ovsdbInventoryService) {
141 this.ovsdbInventoryService = ovsdbInventoryService;
144 public void unsetInventoryServiceInternal(OvsdbInventoryService ovsdbInventoryService) {
145 if (this.ovsdbInventoryService == ovsdbInventoryService) {
146 this.ovsdbInventoryService = null;
150 private IClusterGlobalServices clusterServices;
152 public void setClusterServices(IClusterGlobalServices i) {
153 this.clusterServices = i;
156 public void unsetClusterServices(IClusterGlobalServices i) {
157 if (this.clusterServices == i) {
158 this.clusterServices = null;
162 private Connection getConnection (Node node) {
163 Connection connection = connectionService.getConnection(node);
164 if (connection == null || !connection.getClient().isActive()) {
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
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));
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);
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
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);
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
211 handleSpecialInsertCase(client, databaseName, childTable, namedUuid, row, transactionBuilder);
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
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];
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.
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};
247 Connection connection = connectionService.getConnection(node);
248 OvsdbClient client = connection.getClient();
250 if (parentUuid == null) {
251 parentUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
253 logger.debug("insertRow Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
254 client.getConnectionInfo(), tableName, parentColumn[0], parentColumn[1], parentUuid, row);
256 DatabaseSchema dbSchema = client.getDatabaseSchema(OvsVswitchdSchemaConstants.DATABASE_NAME);
257 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
259 String namedUuid = "Transaction_"+ tableName;
260 this.processTypedInsertTransaction(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName,
261 parentColumn[0], parentUuid, parentColumn[1], namedUuid,
262 row, transactionBuilder);
264 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
265 List<OperationResult> operationResults;
267 operationResults = results.get();
268 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
269 return new StatusWithUuid(StatusCode.INTERNALERROR);
271 for (OperationResult result : operationResults) {
272 if (result.getError() != null) {
273 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
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());
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);
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);
298 if (parentColumn != null) {
299 TableSchema<GenericTableSchema> parentTableSchema = dbSchema.table(parentTable, GenericTableSchema.class);
300 ColumnSchema<GenericTableSchema, UUID> parentColumnSchema = parentTableSchema.column(parentColumn, UUID.class);
302 .add(op.mutate(parentTableSchema)
303 .addMutation(parentColumnSchema, Mutator.DELETE, new UUID(uuid))
304 .where(parentColumnSchema.opIncludes(new UUID(uuid)))
308 ColumnSchema<GenericTableSchema, UUID> _uuid = childTableSchema.column("_uuid", UUID.class);
309 transactionBuilder.add(op.delete(childTableSchema)
310 .where(_uuid.opEqual(new UUID(uuid)))
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();
321 String[] parentColumn = OvsVswitchdSchemaConstants.getParentColumnToMutate(tableName);
322 if (parentColumn == null) {
323 parentColumn = new String[]{null, null};
326 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
327 client.getConnectionInfo(), databaseName, tableName, uuid, parentColumn[0], parentColumn[1]);
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);
334 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
335 List<OperationResult> operationResults;
337 operationResults = results.get();
338 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
339 return new StatusWithUuid(StatusCode.INTERNALERROR);
341 for (OperationResult result : operationResults) {
342 if (result.getError() != null) {
343 return new StatusWithUuid(StatusCode.BADREQUEST, result.getError());
346 } catch (InterruptedException | ExecutionException e) {
347 // TODO Auto-generated catch block
351 return new Status(StatusCode.SUCCESS);
356 public ConcurrentMap<String, Row> getRows(Node node, String tableName) {
357 ConcurrentMap<String, Row> ovsTable = ovsdbInventoryService.getTableCache(node, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
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);
371 public List<String> getTables(Node node) {
372 return this.getTables(node, OvsVswitchdSchemaConstants.DATABASE_NAME);
375 private List<InetAddress> getControllerIPAddresses(Connection connection) {
376 List<InetAddress> controllers = null;
377 InetAddress controllerIP = null;
379 controllers = new ArrayList<InetAddress>();
380 String addressString = System.getProperty("ovsdb.controller.address");
382 if (addressString != null) {
384 controllerIP = InetAddress.getByName(addressString);
385 if (controllerIP != null) {
386 controllers.add(controllerIP);
389 } catch (UnknownHostException e) {
390 logger.error("Host {} is invalid", addressString);
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())) {
408 addressString = System.getProperty("of.address");
410 if (addressString != null) {
412 controllerIP = InetAddress.getByName(addressString);
413 if (controllerIP != null) {
414 controllers.add(controllerIP);
417 } catch (UnknownHostException e) {
418 logger.error("Host {} is invalid", addressString);
423 controllerIP = connection.getClient().getConnectionInfo().getLocalAddress();
424 controllers.add(controllerIP);
426 } catch (Exception e) {
427 logger.debug("Invalid connection provided to getControllerIPAddresses", e);
432 private short getControllerOFPort() {
433 Short defaultOpenFlowPort = 6633;
434 Short openFlowPort = defaultOpenFlowPort;
435 String portString = System.getProperty("of.listenPort");
436 if (portString != null) {
438 openFlowPort = Short.decode(portString).shortValue();
439 } catch (NumberFormatException e) {
440 logger.warn("Invalid port:{}, use default({})", portString,
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);
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);
461 return currentControllerTargets;
465 public Boolean setOFController(Node node, String bridgeUUID) throws InterruptedException, ExecutionException {
466 Connection connection = this.getConnection(node);
467 if (connection == null) {
471 Bridge bridge = connection.getClient().createTypedRowWrapper(Bridge.class);
473 Status updateOperationStatus = null;
475 OvsdbSet<String> protocols = new OvsdbSet<String>();
477 String ofVersion = System.getProperty("ovsdb.of.version", OPENFLOW_13);
480 protocols.add("OpenFlow10");
485 protocols.add("OpenFlow13");
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);
494 } catch (SchemaVersionMismatchException e){
495 logger.debug(e.toString());
498 // If we fail to update the protocols
499 if (updateOperationStatus != null && !updateOperationStatus.isSuccess()) {
500 return updateOperationStatus.isSuccess();
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();
513 // get current controller targets, iff this is the first iteration
514 if (currControllerTargets == null) {
515 currControllerTargets = getCurrentControllerTargets(node, controllerTableName);
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());
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);
528 if (status != null) {
529 return status.isSuccess();
536 public Boolean setBridgeOFController(Node node, String bridgeIdentifier) {
537 if (connectionService == null) {
538 logger.error("Couldn't refer to the ConnectionService");
543 Connection connection = connectionService.getConnection(node);
544 Bridge bridge = connection.getClient().getTypedRowWrapper(Bridge.class, null);
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);
553 } catch(Exception e) {
554 logger.error("Error in setBridgeOFController()",e);
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();
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);
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);
585 public void _ovsconnect (CommandInterpreter ci) {
586 String bridgeName = ci.nextArgument();
587 if (bridgeName == null) {
588 ci.println("Please enter Bridge Name");
592 String ovsdbserver = ci.nextArgument();
593 if (ovsdbserver == null) {
594 ci.println("Please enter valid IP-Address");
598 InetAddress.getByName(ovsdbserver);
599 } catch (UnknownHostException e) {
600 logger.error("Unable to resolve " + ovsdbserver, e);
601 ci.println("Please enter valid IP-Address");
604 String port = ci.nextArgument();
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");
618 public void _addBridge (CommandInterpreter ci) {
619 String nodeName = ci.nextArgument();
620 if (nodeName == null) {
621 ci.println("Please enter Node Name");
624 String bridgeName = ci.nextArgument();
625 if (bridgeName == null) {
626 ci.println("Please enter Bridge Name");
631 Node node = Node.fromString(nodeName);
633 ci.println("Invalid Node");
636 status = this.createBridgeDomain(node, bridgeName, null);
637 ci.println("Bridge creation status : "+status.toString());
640 public void _getBridgeDomains (CommandInterpreter ci) {
641 String nodeName = ci.nextArgument();
642 if (nodeName == null) {
643 ci.println("Please enter Node Name");
647 List<String> brlist = new ArrayList<String>();
648 Node node = Node.fromString(nodeName);
649 brlist = this.getBridgeDomains(node);
651 ci.println("Invalid Node");
654 ci.println("Existing Bridges: "+brlist.toString());
657 public void _deleteBridgeDomain (CommandInterpreter ci) {
658 String nodeName = ci.nextArgument();
659 if (nodeName == null) {
660 ci.println("Please enter Node Name");
663 String bridgeName = ci.nextArgument();
664 if (bridgeName == null) {
665 ci.println("Please enter Bridge Name");
669 Node node = Node.fromString(nodeName);
671 ci.println("Invalid Node");
674 status = this.deleteBridgeDomain(node, bridgeName);
675 ci.println("Bridge deletion status : "+status.toString());
678 public void _addPort (CommandInterpreter ci) {
679 String nodeName = ci.nextArgument();
680 if (nodeName == null) {
681 ci.println("Please enter Node Name");
685 String bridgeName = ci.nextArgument();
686 if (bridgeName == null) {
687 ci.println("Please enter Bridge Name");
691 String portName = ci.nextArgument();
692 if (portName == null) {
693 ci.println("Please enter Port Name");
697 String type = ci.nextArgument();
699 Map<String, String> configs = new HashMap<String, String>();
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);
708 Map<ConfigConstants, Object> customConfigs = null;
710 customConfigs = new HashMap<ConfigConstants, Object>();
711 customConfigs.put(ConfigConstants.TYPE, type);
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());
720 Node node = Node.fromString(nodeName);
722 ci.println("Invalid Node");
725 status = this.addPort(node, bridgeName, portName, customConfigs);
726 ci.println("Port creation status : "+status.toString());
729 public void _deletePort (CommandInterpreter ci) {
730 String nodeName = ci.nextArgument();
731 if (nodeName == null) {
732 ci.println("Please enter Node Name");
736 String bridgeName = ci.nextArgument();
737 if (bridgeName == null) {
738 ci.println("Please enter Bridge Name");
742 String portName = ci.nextArgument();
743 if (portName == null) {
744 ci.println("Please enter Port Name");
749 Node node = Node.fromString(nodeName);
751 ci.println("Invalid Node");
754 status = this.deletePort(node, bridgeName, portName);
755 ci.println("Port deletion status : "+status.toString());
758 public void _addPortVlan (CommandInterpreter ci) {
759 String nodeName = ci.nextArgument();
760 if (nodeName == null) {
761 ci.println("Please enter Node Name");
765 String bridgeName = ci.nextArgument();
766 if (bridgeName == null) {
767 ci.println("Please enter Bridge Name");
771 String portName = ci.nextArgument();
772 if (portName == null) {
773 ci.println("Please enter Port Name");
777 String vlan = ci.nextArgument();
779 ci.println("Please enter Valid Vlan");
783 Integer.parseInt(vlan);
784 } catch (NumberFormatException e) {
785 ci.println("Please enter Valid Vlan");
790 Map<ConfigConstants, Object> configs = new HashMap<ConfigConstants, Object>();
791 configs.put(ConfigConstants.TYPE, "VLAN");
792 configs.put(ConfigConstants.VLAN, vlan);
795 Node node = Node.fromString(nodeName);
797 ci.println("Invalid Node");
800 status = this.addPort(node, bridgeName, portName, configs);
801 ci.println("Port creation status : "+status.toString());
804 public void _addTunnel (CommandInterpreter ci) {
805 String nodeName = ci.nextArgument();
806 if (nodeName == null) {
807 ci.println("Please enter Node Name");
811 String bridgeName = ci.nextArgument();
812 if (bridgeName == null) {
813 ci.println("Please enter Bridge Name");
817 String portName = ci.nextArgument();
818 if (portName == null) {
819 ci.println("Please enter Port Name");
823 String tunnelType = ci.nextArgument();
824 if (tunnelType == null) {
825 ci.println("Please enter Tunnel Type");
829 String remoteIp = ci.nextArgument();
830 if (remoteIp == null) {
831 ci.println("Please enter valid Remote IP Address");
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");
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);
849 Node node = Node.fromString(nodeName);
851 ci.println("Invalid Node");
854 status = this.addPort(node, bridgeName, portName, configs);
855 ci.println("Port creation status : "+status.toString());
858 public void _printCache (CommandInterpreter ci) {
859 String nodeName = ci.nextArgument();
860 if (nodeName == null) {
861 ci.println("Please enter Node Name");
864 Node node = Node.fromString(nodeName);
866 ci.println("Invalid Node");
869 ovsdbInventoryService.printCache(node);
872 public void _forceConnect (CommandInterpreter ci) {
873 String force = ci.nextArgument();
874 if (force.equalsIgnoreCase("YES")) {
877 else if (force.equalsIgnoreCase("NO")) {
878 forceConnect = false;
881 ci.println("Please enter YES or NO.");
883 ci.println("Current ForceConnect State : "+forceConnect);
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();
905 * @param node Node serving this configuration service
906 * @param bridgeIdentifier String representation of a Bridge Connector
907 * @return Bridge Connector configurations
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);
917 String ovsTableUuid = this.getSpecialCaseParentUUID(node, OvsVswitchdSchemaConstants.DATABASE_NAME, bridge.getSchema().getName());
918 return this.insertRow(node, bridge.getSchema().getName(), ovsTableUuid, bridge.getRow());
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
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();
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);
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;
945 if (bridge.getName() == null || !bridge.getName().equals(bridgeIdentifier)) {
946 return new Status(StatusCode.NOTFOUND);
949 Map<String, String> options = 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));
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)));
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());
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.
984 } catch (InterruptedException e) {
985 // TODO Auto-generated catch block
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);
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;
1000 if (interfaceRow.getName() == null || !interfaceRow.getName().equals(portIdentifier)) {
1001 return new Status(StatusCode.NOTFOUND);
1003 Interface updateInterface = client.createTypedRowWrapper(Interface.class);
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());
1015 if (options != null) {
1016 updateInterface.setOptions(options);
1019 Status intfStatus = null;
1020 intfStatus = this.updateRow(node, interfaceRow.getSchema().getName(), portStatus.getUuid().toString(),
1021 interfaceRow.getUuid().toString(), updateInterface.getRow());
1023 if (intfStatus.isSuccess()) return portStatus;
1028 * Implements the OVS Connection for Managers
1030 * @param node Node serving this configuration service
1031 * @param managerip String Representing IP and connection types
1033 @SuppressWarnings("unchecked")
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));
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) {
1046 String ovsTableUuid = (String)row.keySet().toArray()[0];
1048 Status status = this.insertRow(node, manager.getSchema().getName(), ovsTableUuid, manager.getRow());
1049 return status.isSuccess();
1054 public Status addBridgeDomainConfig(Node node, String bridgeIdentfier,
1055 Map<ConfigConstants, Object> configs) {
1056 String mgmt = (String)configs.get(ConfigConstants.MGMT);
1058 if (setManager(node, mgmt)) return new Status(StatusCode.SUCCESS);
1060 return new Status(StatusCode.BADREQUEST);
1065 public Status deletePort(Node node, String bridgeIdentifier, String portIdentifier) {
1066 Connection connection = connectionService.getConnection(node);
1067 OvsdbClient client = connection.getClient();
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);
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;
1079 if (port.getName() == null || !port.getName().equals(portIdentifier)) {
1080 return new Status(StatusCode.NOTFOUND);
1082 return this.deleteRow(node, port.getSchema().getName(), port.getUuid().toString());
1087 public Status deleteBridgeDomain(Node node, String bridgeIdentifier) {
1088 Connection connection = connectionService.getConnection(node);
1089 OvsdbClient client = connection.getClient();
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);
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;
1101 if (bridge.getName() == null || !bridge.getName().equals(bridgeIdentifier)) {
1102 return new Status(StatusCode.NOTFOUND);
1104 return this.deleteRow(node, bridge.getSchema().getName(), bridge.getUuid().toString());
1108 public List<String> getBridgeDomains(Node node) {
1109 if (connectionService == null) {
1110 logger.error("Couldn't refer to the ConnectionService");
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());
1128 public NodeConnector getNodeConnector(Node arg0, String arg1, String arg2) {
1134 public Status addPortConfig(Node node, String bridgeIdentifier, String portIdentifier,
1135 Map<ConfigConstants, Object> configs) {
1136 // TODO Auto-generated method stub
1142 public Node getBridgeDomainNode(Node node, String bridgeIdentifier) {
1143 // TODO Auto-generated method stub
1149 public Map<ConfigConstants, Object> getPortConfigs(Node node, String bridgeIdentifier,
1150 String portIdentifier) {
1151 // TODO Auto-generated method stub
1157 public Status removeBridgeDomainConfig(Node node, String bridgeIdentifier,
1158 Map<ConfigConstants, Object> configs) {
1159 // TODO Auto-generated method stub
1165 public Status removePortConfig(Node node, String bridgeIdentifier, String portIdentifier,
1166 Map<ConfigConstants, Object> configs) {
1167 // TODO Auto-generated method stub
1173 public Map<ConfigConstants, Object> getBridgeDomainConfigs(Node node, String bridgeIdentifier) {
1174 // TODO Auto-generated method stub
1179 // SCHEMA-INDEPENDENT Configuration Service APIs
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) {
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;
1204 throw new OvsdbPluginException("Multiple Referencing Columns for "+ childTableName +" on "+ parentTableSchema.getName());
1209 if (refColumn != null) {
1212 throw new OvsdbPluginException("No Referencing Column for "+childTableName+" on "+parentTableSchema.getName());
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.
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));
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);
1235 .add(op.mutate(parentTableSchema)
1236 .addMutation(parentColumnSchema, Mutator.INSERT, new UUID(namedUuid))
1237 .where(_uuid.opEqual(parentUuid))
1243 * insert a Row in a Table of a specified Database Schema.
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.
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
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);
1267 Row<GenericTableSchema> processedRow = this.insertTree(node, databaseName, tableName, parentTable, parentUuid, parentColumn, row);
1269 ColumnSchema<GenericTableSchema, UUID> _uuid = tableSchema.column("_uuid", UUID.class);
1270 Column<GenericTableSchema, UUID> uuid = processedRow.getColumn(_uuid);
1271 return uuid.getData();
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.
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.
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
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);
1300 * inserts a Tree of Rows in multiple Tables that has parent-child relationships referenced through the OVSDB schema's refTable construct
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
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();
1318 if (databaseName == null || tableName == null) {
1319 throw new OvsdbPluginException("databaseName, tableName and parentUuid are Mandatory Parameters");
1322 if (parentTable == null && parentUuid != null) {
1323 parentTable = this.getTableNameForRowUuid(node, databaseName, parentUuid);
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);
1332 logger.debug("insertTree Connection : {} Table : {} ParentTable : {} Parent Column: {} Parent UUID : {} Row : {}",
1333 client.getConnectionInfo(), tableName, parentTable, parentColumn, parentUuid, row);
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);
1340 String namedUuid = "Transaction_"+ tableName;
1341 this.processInsertTransaction(client, databaseName, tableName, parentTable, parentUuid,
1342 parentColumn, namedUuid, row, transactionBuilder);
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()));
1355 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1356 List<OperationResult> operationResults;
1358 operationResults = results.get();
1359 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1360 throw new OvsdbPluginException("Insert Operation Failed");
1362 for (OperationResult result : operationResults) {
1363 if (result.getError() != null) {
1364 throw new OvsdbPluginException("Insert Operation Failed with Error : "+result.getError().toString());
1367 return getNormalizedRow(dbSchema, tableName, row, referencedRows, operationResults, referencedRowsInsertIndex);
1368 } catch (InterruptedException | ExecutionException e) {
1369 throw new OvsdbPluginException("Exception : "+e.getLocalizedMessage());
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}
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
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);
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.
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.
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);
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());
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);
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());
1447 modifiedSet.add(obj);
1450 column.setData(modifiedSet);
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.
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.
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);
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());
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();
1510 logger.debug("updateRow : Connection : {} databaseName : {} tableName : {} rowUUID : {} row : {}",
1511 client.getConnectionInfo(), databaseName, tableName, rowUuid, row.toString());
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))
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());
1528 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1529 throw new OvsdbPluginException("Failed to update row. Please check OVS logs for more info.");
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());
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();
1544 if (parentTable == null && parentRowUuid != null) {
1545 parentTable = this.getTableNameForRowUuid(node, databaseName, parentRowUuid);
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);
1554 logger.debug("deleteRow : Connection : {} databaseName : {} tableName : {} Uuid : {} ParentTable : {} ParentColumn : {}",
1555 client.getConnectionInfo(), databaseName, tableName, rowUuid, parentTable, parentColumn);
1557 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
1558 TransactionBuilder transactionBuilder = client.transactBuilder(dbSchema);
1559 this.processDeleteTransaction(client, databaseName, tableName,
1560 parentTable, parentColumn, rowUuid.toString(), transactionBuilder);
1562 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
1563 List<OperationResult> operationResults;
1565 operationResults = results.get();
1566 if (operationResults.isEmpty() || (transactionBuilder.getOperations().size() != operationResults.size())) {
1567 throw new OvsdbPluginException("Delete Operation Failed");
1569 for (OperationResult result : operationResults) {
1570 if (result.getError() != null) {
1571 throw new OvsdbPluginException("Delete Operation Failed with Error : "+result.getError().toString());
1574 } catch (InterruptedException | ExecutionException e) {
1575 // TODO Auto-generated catch block
1576 e.printStackTrace();
1581 public void deleteRow(Node node, String databaseName, String tableName, UUID rowUuid) throws OvsdbPluginException {
1582 this.deleteRow(node, databaseName, tableName, null, null, null, rowUuid);
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);
1590 return rows.get(uuid);
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));
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);
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());