2 * Copyright (C) 2014 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 : Sam Hague, Matt Oswalt
10 package org.opendaylight.ovsdb.integrationtest.schema.hardwarevtep;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
18 import static org.ops4j.pax.exam.CoreOptions.junitBundles;
19 import static org.ops4j.pax.exam.CoreOptions.options;
20 import static org.ops4j.pax.exam.CoreOptions.propagateSystemProperty;
21 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Sets;
25 import com.google.common.util.concurrent.ListenableFuture;
26 import java.io.IOException;
27 import java.util.HashMap;
28 import java.util.List;
31 import java.util.concurrent.ExecutionException;
32 import javax.inject.Inject;
33 import junit.framework.Assert;
34 import org.junit.Assume;
35 import org.junit.Before;
36 import org.junit.Rule;
37 import org.junit.Test;
38 import org.junit.rules.TestRule;
39 import org.junit.rules.TestWatcher;
40 import org.junit.runner.Description;
41 import org.junit.runner.RunWith;
42 import org.opendaylight.ovsdb.integrationtest.ConfigurationBundles;
43 import org.opendaylight.ovsdb.integrationtest.OvsdbIntegrationTestBase;
44 import org.opendaylight.ovsdb.lib.MonitorCallBack;
45 import org.opendaylight.ovsdb.lib.OvsdbClient;
46 import org.opendaylight.ovsdb.lib.message.MonitorRequest;
47 import org.opendaylight.ovsdb.lib.message.MonitorRequestBuilder;
48 import org.opendaylight.ovsdb.lib.message.MonitorSelect;
49 import org.opendaylight.ovsdb.lib.message.TableUpdate;
50 import org.opendaylight.ovsdb.lib.message.TableUpdates;
51 import org.opendaylight.ovsdb.lib.notation.Mutator;
52 import org.opendaylight.ovsdb.lib.notation.Row;
53 import org.opendaylight.ovsdb.lib.notation.UUID;
54 import org.opendaylight.ovsdb.lib.operations.OperationResult;
55 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
56 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
57 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
58 import org.opendaylight.ovsdb.lib.schema.TableSchema;
59 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
60 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
61 import org.opendaylight.ovsdb.schema.hardwarevtep.Manager;
62 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
63 import org.ops4j.pax.exam.Configuration;
64 import org.ops4j.pax.exam.Option;
65 import org.ops4j.pax.exam.junit.PaxExam;
66 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
67 import org.ops4j.pax.exam.spi.reactors.PerSuite;
68 import org.ops4j.pax.exam.util.PathUtils;
69 import org.osgi.framework.BundleContext;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
73 @RunWith(PaxExam.class)
74 @ExamReactorStrategy(PerSuite.class)
75 public class HardwareVTEPIT extends OvsdbIntegrationTestBase {
76 private static final Logger LOG = LoggerFactory.getLogger(HardwareVTEPIT.class);
77 private static boolean monitorReady = false;
78 private static boolean schemaSupported = false;
79 private static final String ASSERT_TRANS_ERROR = "Transaction should not have errors";
80 private static final String ASSERT_TRANS_RESULT_EMPTY = "Transaction should not be empty";
81 private static final String ASSERT_TRANS_OPERATION_COUNT = "Transaction should match number of operations";
82 private static final String ASSERT_TRANS_UUID = "Transaction UUID should not be null";
83 private UUID testManagerUuid = null;
85 private static Map<String, Map<UUID, Row>> tableCache = new HashMap<>();
86 private static Map<String, Map<UUID, Row>> getTableCache () {
90 private static OvsdbClient ovsdbClient;
91 private OvsdbClient getClient () {
95 private static DatabaseSchema dbSchema;
96 private DatabaseSchema getDbSchema () {
101 private BundleContext bc;
104 public Option[] config() throws Exception {
106 systemProperty("logback.configurationFile").value(
107 "file:" + PathUtils.getBaseDir()
108 + "/src/test/resources/logback.xml"
110 // To start OSGi console for inspection remotely
111 systemProperty("osgi.console").value("2401"),
113 propagateSystemProperty("ovsdbserver.ipaddress"),
114 propagateSystemProperty("ovsdbserver.port"),
116 ConfigurationBundles.controllerBundles(),
117 ConfigurationBundles.ovsdbLibraryBundles(),
118 ConfigurationBundles.ovsdbDefaultSchemaBundles(),
124 * Method adds a log as each test method starts and finishes. This is useful when
125 * the test suite is used because the suites only print a final summary.
128 public TestRule watcher = new TestWatcher() {
130 protected void starting(Description description) {
131 LOG.info("TestWatcher: Starting test: {}",
132 description.getDisplayName());
136 protected void finished(Description description) {
137 LOG.info("TestWatcher: Finished test: {}", description.getDisplayName());
142 public void setUp () throws ExecutionException, InterruptedException, IOException {
144 assertTrue(HARDWARE_VTEP + " is required.", checkSchema(HARDWARE_VTEP));
145 assertTrue("Failed to monitor tables", monitorTables());
148 public boolean checkSchema (String schema) {
149 if (schemaSupported) {
150 LOG.info("Schema ({}) is supported", schema);
154 ovsdbClient = getTestConnection();
155 assertNotNull("Invalid Client. Check connection params", ovsdbClient);
156 //Thread.sleep(3000); // Wait for a few seconds to get the Schema exchange done
157 if (isSchemaSupported(ovsdbClient, schema)) {
158 dbSchema = ovsdbClient.getSchema(schema).get();
159 assertNotNull(dbSchema);
160 LOG.info("{} schema in {} with tables: {}",
161 schema, ovsdbClient.getConnectionInfo(), dbSchema.getTables());
162 schemaSupported = true;
165 } catch (Exception e) {
166 fail("Exception : "+e.getMessage());
169 LOG.info("Schema ({}) is not supported", schema);
173 public UUID getOpenVSwitchTableUuid (OvsdbClient ovs, Map<String, Map<UUID, Row>> tableCache) {
174 OpenVSwitch openVSwitch = getClient().getTypedRowWrapper(OpenVSwitch.class, null);
175 Map<UUID, Row> ovsTable = tableCache.get(openVSwitch.getSchema().getName());
176 if (ovsTable != null) {
177 if (ovsTable.keySet().size() >= 1) {
178 return (UUID)ovsTable.keySet().toArray()[0];
184 public UUID getGlobalTableUuid(OvsdbClient ovs, Map<String, Map<UUID, Row>> tableCache) {
185 Global glbl = getClient().getTypedRowWrapper(Global.class, null);
186 Map<UUID, Row> glblTbl = tableCache.get(glbl.getSchema().getName());
187 if (glblTbl != null) {
188 if (glblTbl.keySet().size() >= 1) {
189 return (UUID)glblTbl.keySet().toArray()[0];
195 public boolean isSchemaSupported (OvsdbClient client, String schema) throws ExecutionException, InterruptedException {
196 ListenableFuture<List<String>> databases = client.getDatabases();
197 List<String> dbNames = databases.get();
198 assertNotNull(dbNames);
199 if (dbNames.contains(schema)) {
207 * As per RFC 7047, section 4.1.5, if a Monitor request is sent without any columns, the update response will not include
209 * ----------------------------------------------------------------------------------------------------------------------------------
210 * Each <monitor-request> specifies one or more columns and the manner in which the columns (or the entire table) are to be monitored.
211 * The "columns" member specifies the columns whose values are monitored. It MUST NOT contain duplicates.
212 * If "columns" is omitted, all columns in the table, except for "_uuid", are monitored.
213 * ----------------------------------------------------------------------------------------------------------------------------------
214 * In order to overcome this limitation, this method
216 * @return MonitorRequest that includes all the Bridge Columns including _uuid
218 public <T extends TypedBaseTable<GenericTableSchema>> MonitorRequest<GenericTableSchema> getAllColumnsMonitorRequest (Class <T> klazz) {
219 TypedBaseTable<GenericTableSchema> table = getClient().createTypedRowWrapper(klazz);
220 GenericTableSchema tableSchema = table.getSchema();
221 Set<String> columns = tableSchema.getColumns();
222 MonitorRequestBuilder<GenericTableSchema> bridgeBuilder = MonitorRequestBuilder.builder(table.getSchema());
223 for (String column : columns) {
224 bridgeBuilder.addColumn(column);
226 return bridgeBuilder.with(new MonitorSelect(true, true, true, true)).build();
229 public <T extends TableSchema<T>> MonitorRequest<T> getAllColumnsMonitorRequest (T tableSchema) {
230 Set<String> columns = tableSchema.getColumns();
231 MonitorRequestBuilder<T> monitorBuilder = MonitorRequestBuilder.builder(tableSchema);
232 for (String column : columns) {
233 monitorBuilder.addColumn(column);
235 return monitorBuilder.with(new MonitorSelect(true, true, true, true)).build();
238 public boolean monitorTables () throws ExecutionException, InterruptedException, IOException {
240 LOG.info("Monitoring is already initialized.");
244 assertNotNull(getDbSchema());
246 List<MonitorRequest<GenericTableSchema>> monitorRequests = Lists.newArrayList();
247 Set<String> tables = getDbSchema().getTables();
248 assertNotNull("ovsdb tables should not be null", tables);
250 for (String tableName : tables) {
251 GenericTableSchema tableSchema = getDbSchema().table(tableName, GenericTableSchema.class);
252 monitorRequests.add(this.getAllColumnsMonitorRequest(tableSchema));
254 TableUpdates updates = getClient().monitor(getDbSchema(), monitorRequests, new UpdateMonitor());
255 assertNotNull(updates);
256 this.updateTableCache(updates);
259 LOG.info("Monitoring is initialized.");
263 private void updateTableCache (TableUpdates updates) {
264 for (String tableName : updates.getUpdates().keySet()) {
265 Map<UUID, Row> tUpdate = getTableCache().get(tableName);
266 TableUpdate update = updates.getUpdates().get(tableName);
267 for (UUID uuid : (Set<UUID>)update.getRows().keySet()) {
268 if (update.getNew(uuid) != null) {
269 if (tUpdate == null) {
270 tUpdate = new HashMap<>();
271 getTableCache().put(tableName, tUpdate);
273 tUpdate.put(uuid, update.getNew(uuid));
275 tUpdate.remove(uuid);
281 private class UpdateMonitor implements MonitorCallBack {
283 public void update(TableUpdates result, DatabaseSchema dbSchema) {
284 updateTableCache(result);
288 public void exception(Throwable t) {
289 LOG.error("Exception t = " + t);
293 public List<OperationResult> executeTransaction (TransactionBuilder transactionBuilder, String text)
294 throws ExecutionException, InterruptedException {
295 ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
296 List<OperationResult> operationResults = results.get();
297 LOG.info("{}: {}", text, operationResults);
298 org.junit.Assert.assertFalse(ASSERT_TRANS_RESULT_EMPTY, operationResults.isEmpty());
299 assertEquals(ASSERT_TRANS_OPERATION_COUNT, transactionBuilder.getOperations().size(), operationResults.size());
300 for (OperationResult result : operationResults) {
301 assertNull(ASSERT_TRANS_ERROR, result.getError());
303 //Thread.sleep(500); // Wait for a few seconds to ensure the cache updates
304 return operationResults;
308 * Create a new manager string in addition to whatever is already there
309 * Will modify the Global table to include the UUID to the new Manager row
311 public void managerInsert () throws ExecutionException, InterruptedException {
312 //Ensure test only proceeds if HW VTEP is supported
313 Assume.assumeTrue(isSchemaSupported(getClient(), HARDWARE_VTEP));
315 //proceed only if schema was already retrieved successfully
316 Assert.assertNotNull(getDbSchema());
318 //create new manager and set target string
319 Manager manager = getClient().createTypedRowWrapper(Manager.class);
320 manager.setTarget("ptcp:6641");
322 String transactionUuidStr = "foobar";
324 Global glbl = this.getClient().createTypedRowWrapper(Global.class);
325 glbl.setManagers(Sets.newHashSet(new UUID(transactionUuidStr)));
327 TransactionBuilder transactionBuilder = getClient().transactBuilder(getDbSchema())
328 .add(op.insert(manager.getSchema())
329 .withId(transactionUuidStr)
330 .value(manager.getTargetColumn()))
331 .add(op.comment("Manager: Inserting " + transactionUuidStr))
332 .add(op.mutate(glbl.getSchema())
333 .addMutation(glbl.getManagersColumn().getSchema(), Mutator.INSERT,
334 glbl.getManagersColumn().getData()))
335 .add(op.comment("Global: Mutating " + transactionUuidStr));
337 int insertOperationIndex = 0;
338 List<OperationResult> operationResults = executeTransaction(transactionBuilder,
339 "Manager: Insert and Mutate results");
340 testManagerUuid = operationResults.get(insertOperationIndex).getUuid();
341 assertNotNull(ASSERT_TRANS_UUID, testManagerUuid);
343 // Verify that the local cache was updated with the remote changes
344 Row managerRow = getTableCache().get(manager.getSchema().getName()).get(testManagerUuid);
345 Manager monitoredManager = getClient().getTypedRowWrapper(Manager.class, managerRow);
346 assertEquals(manager.getTargetColumn().getData(), monitoredManager.getTargetColumn().getData());
347 assertNotNull(monitoredManager.getUuid());
348 assertNotNull(monitoredManager.getVersion());
349 assertNotNull(getGlobalTableUuid(getClient(), getTableCache()));
352 public void managerDelete () throws ExecutionException, InterruptedException {
353 Assume.assumeTrue(isSchemaSupported(getClient(), HARDWARE_VTEP));
355 Manager manager = getClient().getTypedRowWrapper(Manager.class, null);
356 Global global = getClient().getTypedRowWrapper(Global.class, null);
358 TransactionBuilder transactionBuilder = getClient().transactBuilder(getDbSchema())
359 .add(op.delete(manager.getSchema())
360 .where(manager.getUuidColumn().getSchema().opEqual(testManagerUuid))
362 .add(op.comment("Manager: Deleting " + testManagerUuid))
363 .add(op.mutate(global.getSchema())
364 .addMutation(global.getManagersColumn().getSchema(), Mutator.DELETE,
365 Sets.newHashSet(testManagerUuid)))
366 .add(op.comment("Global: Mutating " + testManagerUuid))
367 .add(op.commit(true));
369 executeTransaction(transactionBuilder, "Manager delete operation results");
373 public void testManager () throws ExecutionException, InterruptedException {