BUG-5737: enable OVSDB Maven site
[ovsdb.git] / library / it / src / test / java / org / opendaylight / ovsdb / lib / it / LibraryIntegrationTestBase.java
1 /*
2  * Copyright © 2015, 2016 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.ovsdb.lib.it;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.junit.Assume.assumeTrue;
17 import static org.ops4j.pax.exam.CoreOptions.composite;
18 import static org.ops4j.pax.exam.CoreOptions.maven;
19 import static org.ops4j.pax.exam.CoreOptions.propagateSystemProperties;
20 import static org.ops4j.pax.exam.CoreOptions.vmOption;
21 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
22 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
23
24 import com.google.common.collect.Lists;
25 import com.google.common.util.concurrent.ListenableFuture;
26 import java.io.IOException;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
34 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
35 import org.opendaylight.ovsdb.lib.MonitorCallBack;
36 import org.opendaylight.ovsdb.lib.OvsdbClient;
37 import org.opendaylight.ovsdb.lib.message.MonitorRequest;
38 import org.opendaylight.ovsdb.lib.message.MonitorRequestBuilder;
39 import org.opendaylight.ovsdb.lib.message.MonitorSelect;
40 import org.opendaylight.ovsdb.lib.message.TableUpdate;
41 import org.opendaylight.ovsdb.lib.message.TableUpdates;
42 import org.opendaylight.ovsdb.lib.notation.Row;
43 import org.opendaylight.ovsdb.lib.notation.UUID;
44 import org.opendaylight.ovsdb.lib.notation.Version;
45 import org.opendaylight.ovsdb.lib.operations.OperationResult;
46 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
47 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
48 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
49 import org.opendaylight.ovsdb.lib.schema.TableSchema;
50 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
51 import org.ops4j.pax.exam.Configuration;
52 import org.ops4j.pax.exam.Option;
53 import org.ops4j.pax.exam.karaf.options.LogLevelOption;
54 import org.ops4j.pax.exam.options.MavenUrlReference;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Base class for library IT.
60  */
61 public abstract class LibraryIntegrationTestBase extends AbstractMdsalTestBase {
62     private static final Logger LOG = LoggerFactory.getLogger(LibraryIntegrationTestBase.class);
63     protected static final String ASSERT_TRANS_ERROR = "Transaction should not have errors";
64     protected static final String ASSERT_TRANS_RESULT_EMPTY = "Transaction should not be empty";
65     protected static final String ASSERT_TRANS_OPERATION_COUNT = "Transaction should match number of operations";
66     protected static final String ASSERT_TRANS_UUID = "Transaction UUID should not be null";
67     protected static Version schemaVersion;
68     private static boolean schemaSupported = false;
69     protected static OvsdbClient ovsdbClient;
70     private static Map<String, Map<UUID, Row>> tableCache = new HashMap<>();
71     private static boolean monitorReady = false;
72     public String schema;
73
74     protected static Map<String, Map<UUID, Row>> getTableCache() {
75         return tableCache;
76     }
77
78     protected OvsdbClient getClient () {
79         return ovsdbClient;
80     }
81
82     protected static DatabaseSchema dbSchema;
83     protected DatabaseSchema getDbSchema () {
84         return dbSchema;
85     }
86
87     private static AtomicBoolean setup = new AtomicBoolean(false);
88     protected static boolean getSetup() {
89         return setup.get();
90     }
91
92     protected static void setSetup(boolean setup) {
93         LibraryIntegrationTestBase.setup.set(setup);
94     }
95
96     @Override
97     public String getModuleName() {
98         return "library";
99     }
100
101     @Override
102     public String getInstanceName() {
103         return "library-default";
104     }
105
106     @Override
107     public MavenUrlReference getFeatureRepo() {
108         return maven()
109                 .groupId("org.opendaylight.ovsdb")
110                 .artifactId("library-features")
111                 .classifier("features")
112                 .type("xml")
113                 .versionAsInProject();
114     }
115
116     @Override
117     public String getFeatureName() {
118         return "odl-ovsdb-library";
119     }
120
121     @Configuration
122     @Override
123     public Option[] config() {
124         Option[] parentOptions = super.config();
125         Option[] propertiesOptions = getPropertiesOptions();
126         Option[] otherOptions = getOtherOptions();
127         Option[] options = new Option[parentOptions.length + propertiesOptions.length + otherOptions.length];
128         System.arraycopy(parentOptions, 0, options, 0, parentOptions.length);
129         System.arraycopy(propertiesOptions, 0, options, parentOptions.length, propertiesOptions.length);
130         System.arraycopy(otherOptions, 0, options, parentOptions.length + propertiesOptions.length,
131                 otherOptions.length);
132         return options;
133     }
134
135     private Option[] getOtherOptions() {
136         return new Option[] {
137                 vmOption("-javaagent:../jars/org.jacoco.agent.jar=destfile=../../jacoco-it.exec"),
138                 keepRuntimeFolder()
139         };
140     }
141
142     public Option[] getPropertiesOptions() {
143         return new Option[] {
144                 propagateSystemProperties(
145                         LibraryIntegrationTestUtils.SERVER_IPADDRESS,
146                         LibraryIntegrationTestUtils.SERVER_PORT,
147                         LibraryIntegrationTestUtils.CONNECTION_TYPE),
148         };
149     }
150
151     @Override
152     public Option getLoggingOption() {
153         Option option = editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
154                 logConfiguration(getClass()),
155                 LogLevelOption.LogLevel.INFO.name());
156         option = composite(option, super.getLoggingOption());
157         return option;
158     }
159
160     protected BindingAwareBroker.ProviderContext getProviderContext() {
161         BindingAwareBroker.ProviderContext providerContext = null;
162         for (int i=0; i < 60; i++) {
163             LOG.info("Looking for ProviderContext, try {}", i);
164             providerContext = getSession();
165             if (providerContext != null) {
166                 break;
167             } else {
168                 try {
169                     Thread.sleep(1000);
170                 } catch (InterruptedException e) {
171                     LOG.warn("Interrupted while waiting for provider context", e);
172                 }
173             }
174         }
175         assertNotNull("providercontext should not be null", providerContext);
176         /* One more second to let the provider finish initialization */
177         try {
178             Thread.sleep(1000);
179         } catch (InterruptedException e) {
180             LOG.warn("Interrupted while waiting for other provider", e);
181         }
182         return providerContext;
183     }
184
185     public boolean checkSchema (String schema) {
186         if (schemaSupported) {
187             LOG.info("Schema ({}) is supported", schema);
188             return true;
189         }
190         try {
191             ovsdbClient = LibraryIntegrationTestUtils.getTestConnection(this);
192             assertNotNull("Invalid Client. Check connection params", ovsdbClient);
193             if (isSchemaSupported(ovsdbClient, schema)) {
194                 dbSchema = ovsdbClient.getSchema(schema).get();
195                 assertNotNull(dbSchema);
196                 LOG.info("{} schema in {} with tables: {}",
197                         schema, ovsdbClient.getConnectionInfo(), dbSchema.getTables());
198                 schemaSupported = true;
199                 return true;
200             }
201         } catch (Exception e) {
202             fail("Exception: " + e);
203         }
204
205         LOG.info("Schema ({}) is not supported", schema);
206         return false;
207     }
208
209     public boolean isSchemaSupported (String schema) throws ExecutionException,
210             InterruptedException {
211         return isSchemaSupported(ovsdbClient, schema);
212     }
213
214     public boolean isSchemaSupported (OvsdbClient client, String schema) throws ExecutionException, InterruptedException {
215         ListenableFuture<List<String>> databases = client.getDatabases();
216         List<String> dbNames = databases.get();
217         assertNotNull(dbNames);
218         return dbNames.contains(schema);
219     }
220
221     /**
222      * As per RFC 7047, section 4.1.5, if a Monitor request is sent without any columns, the update response will not include
223      * the _uuid column.
224      * ----------------------------------------------------------------------------------------------------------------------------------
225      * Each &lt;monitor-request&gt; specifies one or more columns and the manner in which the columns (or the entire table) are to be monitored.
226      * The "columns" member specifies the columns whose values are monitored. It MUST NOT contain duplicates.
227      * If "columns" is omitted, all columns in the table, except for "_uuid", are monitored.
228      * ----------------------------------------------------------------------------------------------------------------------------------
229      * In order to overcome this limitation, this method
230      *
231      * @return MonitorRequest that includes all the Bridge Columns including _uuid
232      */
233     public <T extends TypedBaseTable<GenericTableSchema>> MonitorRequest getAllColumnsMonitorRequest (Class <T> klazz) {
234         TypedBaseTable<GenericTableSchema> table = getClient().createTypedRowWrapper(klazz);
235         GenericTableSchema tableSchema = table.getSchema();
236         Set<String> columns = tableSchema.getColumns();
237         MonitorRequestBuilder<GenericTableSchema> bridgeBuilder = MonitorRequestBuilder.builder(table.getSchema());
238         for (String column : columns) {
239             bridgeBuilder.addColumn(column);
240         }
241         return bridgeBuilder.with(new MonitorSelect(true, true, true, true)).build();
242     }
243
244     public <T extends TableSchema<T>> MonitorRequest getAllColumnsMonitorRequest (T tableSchema) {
245         Set<String> columns = tableSchema.getColumns();
246         MonitorRequestBuilder<T> monitorBuilder = MonitorRequestBuilder.builder(tableSchema);
247         for (String column : columns) {
248             monitorBuilder.addColumn(column);
249         }
250         return monitorBuilder.with(new MonitorSelect(true, true, true, true)).build();
251     }
252
253     public boolean monitorTables () throws ExecutionException, InterruptedException, IOException {
254         if (monitorReady) {
255             LOG.info("Monitoring is already initialized.");
256             return monitorReady;
257         }
258
259         assertNotNull(getDbSchema());
260
261         List<MonitorRequest> monitorRequests = Lists.newArrayList();
262         Set<String> tables = getDbSchema().getTables();
263         assertNotNull("ovsdb tables should not be null", tables);
264
265         for (String tableName : tables) {
266             GenericTableSchema tableSchema = getDbSchema().table(tableName, GenericTableSchema.class);
267             monitorRequests.add(this.getAllColumnsMonitorRequest(tableSchema));
268         }
269         TableUpdates updates = getClient().monitor(getDbSchema(), monitorRequests, new UpdateMonitor());
270         assertNotNull(updates);
271         this.updateTableCache(updates);
272
273         monitorReady = true;
274         LOG.info("Monitoring is initialized.");
275         return monitorReady;
276     }
277
278     @SuppressWarnings("unchecked")
279     protected void updateTableCache(TableUpdates updates) {
280         for (String tableName : updates.getUpdates().keySet()) {
281             Map<UUID, Row> tUpdate = getTableCache().get(tableName);
282             TableUpdate update = updates.getUpdates().get(tableName);
283             for (UUID uuid : (Set<UUID>)update.getRows().keySet()) {
284                 if (update.getNew(uuid) != null) {
285                     if (tUpdate == null) {
286                         tUpdate = new HashMap<>();
287                         getTableCache().put(tableName, tUpdate);
288                     }
289                     tUpdate.put(uuid, update.getNew(uuid));
290                 } else {
291                     tUpdate.remove(uuid);
292                 }
293             }
294         }
295     }
296
297     public List<OperationResult> executeTransaction (TransactionBuilder transactionBuilder, String text)
298             throws ExecutionException, InterruptedException {
299         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
300         List<OperationResult> operationResults = results.get();
301         LOG.info("{}: {}", text, operationResults);
302         org.junit.Assert.assertFalse(ASSERT_TRANS_RESULT_EMPTY, operationResults.isEmpty());
303         assertEquals(ASSERT_TRANS_OPERATION_COUNT, transactionBuilder.getOperations().size(), operationResults.size());
304         for (OperationResult result : operationResults) {
305             assertNull(ASSERT_TRANS_ERROR, result.getError());
306         }
307         //Thread.sleep(500); // Wait for a few seconds to ensure the cache updates
308         return operationResults;
309     }
310
311     public void setup() throws Exception {
312         if (getSetup()) {
313             LOG.info("Skipping setUp, already initialized");
314             return;
315         }
316
317         try {
318             super.setup();
319         } catch (Exception e) {
320             LOG.warn("Failed to setup test", e);
321             fail("Failed to setup test: " + e);
322         }
323
324         assertNotNull("ProviderContext was not found", getProviderContext());
325         if (schema.equals(LibraryIntegrationTestUtils.OPEN_VSWITCH)) {
326             assertTrue(schema + " is required.", checkSchema(schema));
327         } else {
328             assumeTrue(schema + " is required.", checkSchema(schema));
329         }
330         schemaVersion = getClient().getDatabaseSchema(schema).getVersion();
331         LOG.info("{} schema version = {}", schema, schemaVersion);
332         assertTrue("Failed to monitor tables", monitorTables());
333         setSetup(true);
334     }
335
336     public void setup2() throws Exception {
337         if (getSetup()) {
338             LOG.info("Skipping setUp, already initialized");
339             return;
340         }
341
342         try {
343             super.setup();
344         } catch (Exception e) {
345             LOG.warn("Failed to setup test", e);
346             fail("Failed to setup test: " + e);
347         }
348
349         assertNotNull("ProviderContext was not found", getProviderContext());
350         setSetup(true);
351     }
352
353     private class UpdateMonitor implements MonitorCallBack {
354         @Override
355         public void update(TableUpdates result, DatabaseSchema dbSchema) {
356             updateTableCache(result);
357         }
358
359         @Override
360         public void exception(Throwable t) {
361             LOG.error("Exception t = " + t);
362         }
363     }
364 }