Updating fix for bug 3989 based on discussion in OVSDB meeting
[ovsdb.git] / library / it / src / test / java / org / opendaylight / ovsdb / integrationtest / schema / hardwarevtep / HardwareVTEPIT.java
1 /*
2  *  Copyright (C) 2014 Red Hat, Inc.
3  *
4  *  This program and the accompanying materials are made available under the
5  *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  *  and is available at http://www.eclipse.org/legal/epl-v10.html
7  *
8  *  Authors : Sam Hague, Matt Oswalt
9  */
10 package org.opendaylight.ovsdb.integrationtest.schema.hardwarevtep;
11
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.junit.Assume.assumeTrue;
18 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
19
20 import com.google.common.collect.Lists;
21 import com.google.common.collect.Sets;
22 import com.google.common.util.concurrent.ListenableFuture;
23 import java.io.IOException;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.ExecutionException;
29 import javax.inject.Inject;
30 import org.junit.Assert;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.opendaylight.ovsdb.lib.MonitorCallBack;
35 import org.opendaylight.ovsdb.lib.OvsdbClient;
36 import org.opendaylight.ovsdb.lib.it.LibraryIntegrationTestBase;
37 import org.opendaylight.ovsdb.lib.it.LibraryIntegrationTestUtils;
38 import org.opendaylight.ovsdb.lib.message.MonitorRequest;
39 import org.opendaylight.ovsdb.lib.message.MonitorRequestBuilder;
40 import org.opendaylight.ovsdb.lib.message.MonitorSelect;
41 import org.opendaylight.ovsdb.lib.message.TableUpdate;
42 import org.opendaylight.ovsdb.lib.message.TableUpdates;
43 import org.opendaylight.ovsdb.lib.notation.Mutator;
44 import org.opendaylight.ovsdb.lib.notation.Row;
45 import org.opendaylight.ovsdb.lib.notation.UUID;
46 import org.opendaylight.ovsdb.lib.operations.OperationResult;
47 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
48 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
49 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
50 import org.opendaylight.ovsdb.lib.schema.TableSchema;
51 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
52 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
53 import org.opendaylight.ovsdb.schema.hardwarevtep.Manager;
54 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
55 import org.ops4j.pax.exam.junit.PaxExam;
56 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
57 import org.ops4j.pax.exam.spi.reactors.PerSuite;
58 import org.osgi.framework.BundleContext;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 @RunWith(PaxExam.class)
63 @ExamReactorStrategy(PerSuite.class)
64 public class HardwareVTEPIT  extends LibraryIntegrationTestBase {
65     private static final Logger LOG = LoggerFactory.getLogger(HardwareVTEPIT.class);
66     private static boolean monitorReady = false;
67     private static boolean schemaSupported = false;
68     private static final String ASSERT_TRANS_ERROR = "Transaction should not have errors";
69     private static final String ASSERT_TRANS_RESULT_EMPTY = "Transaction should not be empty";
70     private static final String ASSERT_TRANS_OPERATION_COUNT = "Transaction should match number of operations";
71     private static final String ASSERT_TRANS_UUID = "Transaction UUID should not be null";
72     private UUID testManagerUuid = null;
73
74     private static Map<String, Map<UUID, Row>> tableCache = new HashMap<>();
75     private static Map<String, Map<UUID, Row>> getTableCache () {
76         return tableCache;
77     }
78
79     private static OvsdbClient ovsdbClient;
80     private OvsdbClient getClient () {
81         return ovsdbClient;
82     }
83
84     private static DatabaseSchema dbSchema;
85     private DatabaseSchema getDbSchema () {
86         return dbSchema;
87     }
88
89     @Inject
90     private BundleContext bc;
91
92     @Before
93     public void set() throws Exception {
94         super.setup();
95         assumeTrue(LibraryIntegrationTestUtils.HARDWARE_VTEP + " is required.", checkSchema(LibraryIntegrationTestUtils.HARDWARE_VTEP));
96         assertTrue("Failed to monitor tables", monitorTables());
97         LOG.info("{} schema version = {}", LibraryIntegrationTestUtils.OPEN_VSWITCH_SCHEMA,
98                 getClient().getDatabaseSchema(LibraryIntegrationTestUtils.OPEN_VSWITCH_SCHEMA).getVersion());
99     }
100
101     public boolean checkSchema (String schema) {
102         if (schemaSupported) {
103             LOG.info("Schema ({}) is supported", schema);
104             return true;
105         }
106         try {
107             ovsdbClient = LibraryIntegrationTestUtils.getTestConnection(this);
108             assertNotNull("Invalid Client. Check connection params", ovsdbClient);
109             //Thread.sleep(3000); // Wait for a few seconds to get the Schema exchange done
110             if (isSchemaSupported(ovsdbClient, schema)) {
111                 dbSchema = ovsdbClient.getSchema(schema).get();
112                 assertNotNull(dbSchema);
113                 LOG.info("{} schema in {} with tables: {}",
114                         schema, ovsdbClient.getConnectionInfo(), dbSchema.getTables());
115                 schemaSupported = true;
116                 return true;
117             }
118         } catch (Exception e) {
119             fail("Exception : "+e.getMessage());
120         }
121
122         LOG.info("Schema ({}) is not supported", schema);
123         return false;
124     }
125
126     public UUID getOpenVSwitchTableUuid (OvsdbClient ovs, Map<String, Map<UUID, Row>> tableCache) {
127         OpenVSwitch openVSwitch = getClient().getTypedRowWrapper(OpenVSwitch.class, null);
128         Map<UUID, Row> ovsTable = tableCache.get(openVSwitch.getSchema().getName());
129         if (ovsTable != null) {
130             if (ovsTable.keySet().size() >= 1) {
131                 return (UUID)ovsTable.keySet().toArray()[0];
132             }
133         }
134         return null;
135     }
136
137     public UUID getGlobalTableUuid(OvsdbClient ovs, Map<String, Map<UUID, Row>> tableCache) {
138         Global glbl = getClient().getTypedRowWrapper(Global.class, null);
139         Map<UUID, Row> glblTbl = tableCache.get(glbl.getSchema().getName());
140         if (glblTbl != null) {
141             if (glblTbl.keySet().size() >= 1) {
142                 return (UUID)glblTbl.keySet().toArray()[0];
143             }
144         }
145         return null;
146     }
147
148     public boolean isSchemaSupported (OvsdbClient client, String schema) throws ExecutionException, InterruptedException {
149         ListenableFuture<List<String>> databases = client.getDatabases();
150         List<String> dbNames = databases.get();
151         assertNotNull(dbNames);
152         return dbNames.contains(schema);
153     }
154
155     /**
156      * As per RFC 7047, section 4.1.5, if a Monitor request is sent without any columns, the update response will not include
157      * the _uuid column.
158      * ----------------------------------------------------------------------------------------------------------------------------------
159      * Each <monitor-request> specifies one or more columns and the manner in which the columns (or the entire table) are to be monitored.
160      * The "columns" member specifies the columns whose values are monitored. It MUST NOT contain duplicates.
161      * If "columns" is omitted, all columns in the table, except for "_uuid", are monitored.
162      * ----------------------------------------------------------------------------------------------------------------------------------
163      * In order to overcome this limitation, this method
164      *
165      * @return MonitorRequest that includes all the Bridge Columns including _uuid
166      */
167     public <T extends TypedBaseTable<GenericTableSchema>> MonitorRequest<GenericTableSchema> getAllColumnsMonitorRequest (Class <T> klazz) {
168         TypedBaseTable<GenericTableSchema> table = getClient().createTypedRowWrapper(klazz);
169         GenericTableSchema tableSchema = table.getSchema();
170         Set<String> columns = tableSchema.getColumns();
171         MonitorRequestBuilder<GenericTableSchema> bridgeBuilder = MonitorRequestBuilder.builder(table.getSchema());
172         for (String column : columns) {
173             bridgeBuilder.addColumn(column);
174         }
175         return bridgeBuilder.with(new MonitorSelect(true, true, true, true)).build();
176     }
177
178     public <T extends TableSchema<T>> MonitorRequest<T> getAllColumnsMonitorRequest (T tableSchema) {
179         Set<String> columns = tableSchema.getColumns();
180         MonitorRequestBuilder<T> monitorBuilder = MonitorRequestBuilder.builder(tableSchema);
181         for (String column : columns) {
182             monitorBuilder.addColumn(column);
183         }
184         return monitorBuilder.with(new MonitorSelect(true, true, true, true)).build();
185     }
186
187     public boolean monitorTables () throws ExecutionException, InterruptedException, IOException {
188         if (monitorReady) {
189             LOG.info("Monitoring is already initialized.");
190             return monitorReady;
191         }
192
193         assertNotNull(getDbSchema());
194
195         List<MonitorRequest<GenericTableSchema>> monitorRequests = Lists.newArrayList();
196         Set<String> tables = getDbSchema().getTables();
197         assertNotNull("ovsdb tables should not be null", tables);
198
199         for (String tableName : tables) {
200             GenericTableSchema tableSchema = getDbSchema().table(tableName, GenericTableSchema.class);
201             monitorRequests.add(this.getAllColumnsMonitorRequest(tableSchema));
202         }
203         TableUpdates updates = getClient().monitor(getDbSchema(), monitorRequests, new UpdateMonitor());
204         assertNotNull(updates);
205         this.updateTableCache(updates);
206
207         monitorReady = true;
208         LOG.info("Monitoring is initialized.");
209         return monitorReady;
210     }
211
212     private void updateTableCache (TableUpdates updates) {
213         for (String tableName : updates.getUpdates().keySet()) {
214             Map<UUID, Row> tUpdate = getTableCache().get(tableName);
215             TableUpdate update = updates.getUpdates().get(tableName);
216             for (UUID uuid : (Set<UUID>)update.getRows().keySet()) {
217                 if (update.getNew(uuid) != null) {
218                     if (tUpdate == null) {
219                         tUpdate = new HashMap<>();
220                         getTableCache().put(tableName, tUpdate);
221                     }
222                     tUpdate.put(uuid, update.getNew(uuid));
223                 } else {
224                     tUpdate.remove(uuid);
225                 }
226             }
227         }
228     }
229
230     private class UpdateMonitor implements MonitorCallBack {
231         @Override
232         public void update(TableUpdates result, DatabaseSchema dbSchema) {
233             updateTableCache(result);
234         }
235
236         @Override
237         public void exception(Throwable t) {
238             LOG.error("Exception t = " + t);
239         }
240     }
241
242     public List<OperationResult> executeTransaction (TransactionBuilder transactionBuilder, String text)
243             throws ExecutionException, InterruptedException {
244         ListenableFuture<List<OperationResult>> results = transactionBuilder.execute();
245         List<OperationResult> operationResults = results.get();
246         LOG.info("{}: {}", text, operationResults);
247         org.junit.Assert.assertFalse(ASSERT_TRANS_RESULT_EMPTY, operationResults.isEmpty());
248         assertEquals(ASSERT_TRANS_OPERATION_COUNT, transactionBuilder.getOperations().size(), operationResults.size());
249         for (OperationResult result : operationResults) {
250             assertNull(ASSERT_TRANS_ERROR, result.getError());
251         }
252         //Thread.sleep(500); // Wait for a few seconds to ensure the cache updates
253         return operationResults;
254     }
255
256     /**
257      * Create a new manager string in addition to whatever is already there
258      * Will modify the Global table to include the UUID to the new Manager row
259      */
260     public void managerInsert () throws ExecutionException, InterruptedException {
261         //Ensure test only proceeds if HW VTEP is supported
262         assumeTrue(isSchemaSupported(getClient(), LibraryIntegrationTestUtils.HARDWARE_VTEP));
263
264         //proceed only if schema was already retrieved successfully
265         Assert.assertNotNull(getDbSchema());
266
267         //create new manager and set target string
268         Manager manager = getClient().createTypedRowWrapper(Manager.class);
269         manager.setTarget("ptcp:6641");
270
271         String transactionUuidStr = "foobar";
272
273         Global glbl = this.getClient().createTypedRowWrapper(Global.class);
274         glbl.setManagers(Sets.newHashSet(new UUID(transactionUuidStr)));
275
276         TransactionBuilder transactionBuilder = getClient().transactBuilder(getDbSchema())
277                 .add(op.insert(manager.getSchema())
278                         .withId(transactionUuidStr)
279                         .value(manager.getTargetColumn()))
280                 .add(op.comment("Manager: Inserting " + transactionUuidStr))
281                 .add(op.mutate(glbl.getSchema())
282                         .addMutation(glbl.getManagersColumn().getSchema(), Mutator.INSERT,
283                                 glbl.getManagersColumn().getData()))
284                 .add(op.comment("Global: Mutating " + transactionUuidStr));
285
286         int insertOperationIndex = 0;
287         List<OperationResult> operationResults = executeTransaction(transactionBuilder,
288                 "Manager: Insert and Mutate results");
289         testManagerUuid = operationResults.get(insertOperationIndex).getUuid();
290         assertNotNull(ASSERT_TRANS_UUID, testManagerUuid);
291
292         // Verify that the local cache was updated with the remote changes
293         Row managerRow = getTableCache().get(manager.getSchema().getName()).get(testManagerUuid);
294         Manager monitoredManager = getClient().getTypedRowWrapper(Manager.class, managerRow);
295         assertEquals(manager.getTargetColumn().getData(), monitoredManager.getTargetColumn().getData());
296         assertNotNull(monitoredManager.getUuid());
297         assertNotNull(monitoredManager.getVersion());
298         assertNotNull(getGlobalTableUuid(getClient(), getTableCache()));
299     }
300
301     public void managerDelete () throws ExecutionException, InterruptedException {
302         assumeTrue(isSchemaSupported(getClient(), LibraryIntegrationTestUtils.HARDWARE_VTEP));
303
304         Manager manager = getClient().getTypedRowWrapper(Manager.class, null);
305         Global global = getClient().getTypedRowWrapper(Global.class, null);
306
307         TransactionBuilder transactionBuilder = getClient().transactBuilder(getDbSchema())
308                 .add(op.delete(manager.getSchema())
309                         .where(manager.getUuidColumn().getSchema().opEqual(testManagerUuid))
310                         .build())
311                 .add(op.comment("Manager: Deleting " + testManagerUuid))
312                 .add(op.mutate(global.getSchema())
313                         .addMutation(global.getManagersColumn().getSchema(), Mutator.DELETE,
314                                 Sets.newHashSet(testManagerUuid)))
315                 .add(op.comment("Global: Mutating " + testManagerUuid))
316                 .add(op.commit(true));
317
318         executeTransaction(transactionBuilder, "Manager delete operation results");
319     }
320
321     @Test
322     public void testManager () throws ExecutionException, InterruptedException {
323         managerInsert();
324         managerDelete();
325     }
326 }