Use config for southbound (take 2)
[ovsdb.git] / southbound / southbound-it / src / test / java / org / opendaylight / ovsdb / southbound / it / SouthboundIT.java
1 /*
2  * Copyright (c) 2015 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 package org.opendaylight.ovsdb.southbound.it;
9
10 import static org.junit.Assert.assertNotNull;
11 import static org.junit.Assert.fail;
12 import static org.ops4j.pax.exam.CoreOptions.composite;
13 import static org.ops4j.pax.exam.CoreOptions.maven;
14 import static org.ops4j.pax.exam.CoreOptions.vmOption;
15 import static org.ops4j.pax.exam.CoreOptions.when;
16 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
17 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
18 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
19
20 import com.google.common.collect.ImmutableBiMap;
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Sets;
23
24 import java.io.File;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.ParameterizedType;
27 import java.lang.reflect.Type;
28 import java.net.InetAddress;
29 import java.net.UnknownHostException;
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Properties;
34 import java.util.Set;
35 import java.util.concurrent.ExecutionException;
36 import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Executors;
38 import java.util.concurrent.TimeUnit;
39
40 import javax.inject.Inject;
41
42 import org.junit.Assert;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
47 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
48 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
49 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
50 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
51 import org.opendaylight.ovsdb.southbound.SouthboundProvider;
52 import org.opendaylight.ovsdb.southbound.SouthboundUtil;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeBase;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeBase;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentationBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeName;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeProtocolBase;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbFailModeBase;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentationBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbPortInterfaceAttributes.VlanMode;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentationBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeExternalIds;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeExternalIdsBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeOtherConfigs;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeOtherConfigsBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntryBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntry;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntryBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfoBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.DatapathTypeEntry;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.InterfaceTypeEntryBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIdsBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceOtherConfigs;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceOtherConfigsBuilder;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.Options;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.OptionsBuilder;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.PortExternalIds;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.PortExternalIdsBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.PortOtherConfigs;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.PortOtherConfigsBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.Trunks;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.TrunksBuilder;
95 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
96 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
97 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
98 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
99 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
100 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
101 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
102 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
103 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
104 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
105 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
106 import org.opendaylight.yangtools.concepts.Builder;
107 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
108 import org.ops4j.pax.exam.Configuration;
109 import org.ops4j.pax.exam.Option;
110 import org.ops4j.pax.exam.junit.PaxExam;
111 import org.ops4j.pax.exam.karaf.options.KarafDistributionOption;
112 import org.ops4j.pax.exam.karaf.options.LogLevelOption;
113 import org.ops4j.pax.exam.options.MavenUrlReference;
114 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
115 import org.ops4j.pax.exam.spi.reactors.PerClass;
116 import org.osgi.framework.BundleContext;
117 import org.slf4j.Logger;
118 import org.slf4j.LoggerFactory;
119
120 /**
121  * Integration tests for southbound-impl
122  *
123  * @author Sam Hague (shague@redhat.com)
124  */
125 @RunWith(PaxExam.class)
126 @ExamReactorStrategy(PerClass.class)
127 public class SouthboundIT extends AbstractMdsalTestBase {
128     private static final String NETDEV_DP_TYPE = "netdev";
129     private static final Logger LOG = LoggerFactory.getLogger(SouthboundIT.class);
130     private static final int OVSDB_UPDATE_TIMEOUT = 1000;
131     private static final String FORMAT_STR = "%s_%s_%d";
132     public static final int NUM_THREADS = 1;
133     private static String addressStr;
134     private static int portNumber;
135     private static String connectionType;
136     private static Boolean setup = false;
137     private static MdsalUtils mdsalUtils = null;
138
139     // TODO Constants copied from AbstractConfigTestBase, need to be removed (see TODO below)
140     private static final String PAX_EXAM_UNPACK_DIRECTORY = "target/exam";
141     private static final String KARAF_DEBUG_PORT = "5005";
142     private static final String KARAF_DEBUG_PROP = "karaf.debug";
143     private static final String KEEP_UNPACK_DIRECTORY_PROP = "karaf.keep.unpack";
144
145     @Inject
146     private BundleContext bundleContext;
147
148     @Configuration
149     public Option[] config() {
150         // TODO Figure out how to use the parent Karaf setup, then just use super.config()
151         Option[] options = new Option[] {
152                 when(Boolean.getBoolean(KARAF_DEBUG_PROP))
153                         .useOptions(KarafDistributionOption.debugConfiguration(KARAF_DEBUG_PORT, true)),
154                 karafDistributionConfiguration().frameworkUrl(getKarafDistro())
155                         .unpackDirectory(new File(PAX_EXAM_UNPACK_DIRECTORY))
156                         .useDeployFolder(false),
157                 when(Boolean.getBoolean(KEEP_UNPACK_DIRECTORY_PROP)).useOptions(keepRuntimeFolder()),
158                 // Works only if we don't specify the feature repo and name
159                 getLoggingOption()};
160         Option[] propertyOptions = getPropertiesOptions();
161         Option[] otherOptions = getOtherOptions();
162         Option[] combinedOptions = new Option[options.length + propertyOptions.length + otherOptions.length];
163         System.arraycopy(options, 0, combinedOptions, 0, options.length);
164         System.arraycopy(propertyOptions, 0, combinedOptions, options.length, propertyOptions.length);
165         System.arraycopy(otherOptions, 0, combinedOptions, options.length + propertyOptions.length,
166                 otherOptions.length);
167         return combinedOptions;
168     }
169
170     private Option[] getOtherOptions() {
171         return new Option[] {
172                 vmOption("-javaagent:../jars/org.jacoco.agent.jar=destfile=../../jacoco-it.exec"),
173                 keepRuntimeFolder()
174         };
175     }
176
177     @Override
178     public String getKarafDistro() {
179         return maven()
180                 .groupId("org.opendaylight.ovsdb")
181                 .artifactId("southbound-karaf")
182                 .versionAsInProject()
183                 .type("zip")
184                 .getURL();
185     }
186
187     @Override
188     public String getModuleName() {
189         return "southbound-impl";
190     }
191
192     @Override
193     public String getInstanceName() {
194         return "southbound-default";
195     }
196
197     @Override
198     public MavenUrlReference getFeatureRepo() {
199         return maven()
200                 .groupId("org.opendaylight.ovsdb")
201                 .artifactId("southbound-features")
202                 .classifier("features")
203                 .type("xml")
204                 .versionAsInProject();
205     }
206
207     @Override
208     public String getFeatureName() {
209         return "odl-ovsdb-southbound-impl-ui";
210     }
211
212     protected String usage() {
213         return "Integration Test needs a valid connection configuration as follows :\n"
214                 + "active connection : mvn -Dovsdbserver.ipaddress=x.x.x.x -Dovsdbserver.port=yyyy verify\n"
215                 + "passive connection : mvn -Dovsdbserver.connection=passive verify\n";
216     }
217
218     @Override
219     public Option getLoggingOption() {
220         return composite(
221                 editConfigurationFilePut(SouthboundITConstants.ORG_OPS4J_PAX_LOGGING_CFG,
222                         "log4j.logger.org.opendaylight.ovsdb",
223                         LogLevelOption.LogLevel.TRACE.name()),
224                 super.getLoggingOption());
225     }
226
227     private Option[] getPropertiesOptions() {
228         Properties props = new Properties(System.getProperties());
229         String addressStr = props.getProperty(SouthboundITConstants.SERVER_IPADDRESS,
230                 SouthboundITConstants.DEFAULT_SERVER_IPADDRESS);
231         String portStr = props.getProperty(SouthboundITConstants.SERVER_PORT,
232                 SouthboundITConstants.DEFAULT_SERVER_PORT);
233         String connectionType = props.getProperty(SouthboundITConstants.CONNECTION_TYPE,
234                 SouthboundITConstants.CONNECTION_TYPE_ACTIVE);
235
236         LOG.info("getPropertiesOptions: Using the following properties: mode= {}, ip:port= {}:{}",
237                 connectionType, addressStr, portStr);
238
239         return new Option[] {
240                 editConfigurationFilePut(SouthboundITConstants.CUSTOM_PROPERTIES,
241                         SouthboundITConstants.SERVER_IPADDRESS, addressStr),
242                 editConfigurationFilePut(SouthboundITConstants.CUSTOM_PROPERTIES,
243                         SouthboundITConstants.SERVER_PORT, portStr),
244                 editConfigurationFilePut(SouthboundITConstants.CUSTOM_PROPERTIES,
245                         SouthboundITConstants.CONNECTION_TYPE, connectionType),
246         };
247     }
248
249     @Before
250     @Override
251     public void setup() throws InterruptedException {
252         if (setup) {
253             LOG.info("Skipping setUp, already initialized");
254             return;
255         }
256
257         try {
258             super.setup();
259         } catch (Exception e) {
260             e.printStackTrace();
261         }
262         //dataBroker = getSession().getSALService(DataBroker.class);
263         Thread.sleep(3000);
264         DataBroker dataBroker = SouthboundProvider.getDb();
265         Assert.assertNotNull("db should not be null", dataBroker);
266
267         addressStr = bundleContext.getProperty(SouthboundITConstants.SERVER_IPADDRESS);
268         String portStr = bundleContext.getProperty(SouthboundITConstants.SERVER_PORT);
269         try {
270             portNumber = Integer.parseInt(portStr);
271         } catch (NumberFormatException e) {
272             fail("Invalid port number " + portStr + System.lineSeparator() + usage());
273         }
274         connectionType = bundleContext.getProperty(SouthboundITConstants.CONNECTION_TYPE);
275
276         LOG.info("setUp: Using the following properties: mode= {}, ip:port= {}:{}",
277                 connectionType, addressStr, portNumber);
278         if (connectionType.equalsIgnoreCase(SouthboundITConstants.CONNECTION_TYPE_ACTIVE)) {
279             if (addressStr == null) {
280                 fail(usage());
281             }
282         }
283
284         mdsalUtils = new MdsalUtils(dataBroker);
285         setup = true;
286     }
287
288     /**
289      * Test passive connection mode. The southbound starts in a listening mode waiting for connections on port
290      * 6640. This test will wait for incoming connections for {@link SouthboundITConstants#CONNECTION_INIT_TIMEOUT} ms.
291      *
292      * @throws InterruptedException
293      */
294     @Test
295     public void testPassiveNode() throws InterruptedException {
296         if (connectionType.equalsIgnoreCase(SouthboundITConstants.CONNECTION_TYPE_PASSIVE)) {
297             //Wait for CONNECTION_INIT_TIMEOUT for the Passive connection to be initiated by the ovsdb-server.
298             Thread.sleep(SouthboundITConstants.CONNECTION_INIT_TIMEOUT);
299         }
300     }
301
302     private ConnectionInfo getConnectionInfo(final String addressStr, final int portNumber) {
303         InetAddress inetAddress = null;
304         try {
305             inetAddress = InetAddress.getByName(addressStr);
306         } catch (UnknownHostException e) {
307             fail("Could not resolve " + addressStr + ": " + e);
308         }
309
310         IpAddress address = SouthboundMapper.createIpAddress(inetAddress);
311         PortNumber port = new PortNumber(portNumber);
312
313         final ConnectionInfo connectionInfo = new ConnectionInfoBuilder()
314                 .setRemoteIp(address)
315                 .setRemotePort(port)
316                 .build();
317         LOG.info("connectionInfo: {}", connectionInfo);
318         return connectionInfo;
319     }
320
321     private String connectionInfoToString(final ConnectionInfo connectionInfo) {
322         return new String(connectionInfo.getRemoteIp().getValue()) + ":" + connectionInfo.getRemotePort().getValue();
323     }
324
325     @Test
326     public void testNetworkTopology() throws InterruptedException {
327         NetworkTopology networkTopology = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION,
328                 InstanceIdentifier.create(NetworkTopology.class));
329         Assert.assertNotNull("NetworkTopology could not be found in " + LogicalDatastoreType.CONFIGURATION,
330                 networkTopology);
331
332         networkTopology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
333                 InstanceIdentifier.create(NetworkTopology.class));
334         Assert.assertNotNull("NetworkTopology could not be found in " + LogicalDatastoreType.OPERATIONAL,
335                 networkTopology);
336     }
337
338     @Test
339     public void testOvsdbTopology() throws InterruptedException {
340         InstanceIdentifier<Topology> path = InstanceIdentifier
341                 .create(NetworkTopology.class)
342                 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID));
343
344         Topology topology = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path);
345         Assert.assertNotNull("Topology could not be found in " + LogicalDatastoreType.CONFIGURATION,
346                 topology);
347
348         topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, path);
349
350         Assert.assertNotNull("Topology could not be found in " + LogicalDatastoreType.OPERATIONAL,
351                 topology);
352     }
353
354     private boolean addOvsdbNode(final ConnectionInfo connectionInfo) throws InterruptedException {
355         InstanceIdentifier<Node> iid = createInstanceIdentifier(connectionInfo);
356         // Check that the node doesn't already exist (we don't support connecting twice)
357         Assert.assertNull("The OVSDB node has already been added",
358                 mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, iid));
359         boolean result = mdsalUtils.put(LogicalDatastoreType.CONFIGURATION,
360                 iid,
361                 createNode(connectionInfo));
362         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
363         return result;
364     }
365
366     private InstanceIdentifier<Node> createInstanceIdentifier(
367             ConnectionInfo connectionInfo) {
368         return InstanceIdentifier
369                 .create(NetworkTopology.class)
370                 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
371                 .child(Node.class,
372                         createNodeKey(connectionInfo.getRemoteIp(), connectionInfo.getRemotePort()));
373     }
374
375     private Node getOvsdbNode(final ConnectionInfo connectionInfo) {
376         return mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
377                 createInstanceIdentifier(connectionInfo));
378     }
379
380     private boolean deleteOvsdbNode(final ConnectionInfo connectionInfo) throws InterruptedException {
381         boolean result = mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION,
382                 createInstanceIdentifier(connectionInfo));
383         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
384         return result;
385     }
386
387     private Node connectOvsdbNode(final ConnectionInfo connectionInfo) throws InterruptedException {
388         Assert.assertTrue(addOvsdbNode(connectionInfo));
389         Node node = getOvsdbNode(connectionInfo);
390         Assert.assertNotNull(node);
391         LOG.info("Connected to {}", connectionInfoToString(connectionInfo));
392         return node;
393     }
394
395     private boolean disconnectOvsdbNode(final ConnectionInfo connectionInfo) throws InterruptedException {
396         Assert.assertTrue(deleteOvsdbNode(connectionInfo));
397         Node node = getOvsdbNode(connectionInfo);
398         Assert.assertNull(node);
399         LOG.info("Disconnected from {}", connectionInfoToString(connectionInfo));
400         return true;
401     }
402
403     @Test
404     public void testAddDeleteOvsdbNode() throws InterruptedException {
405         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
406         connectOvsdbNode(connectionInfo);
407         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
408     }
409
410     @Test
411     public void testDpdkSwitch() throws InterruptedException {
412         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
413         Node ovsdbNode = connectOvsdbNode(connectionInfo);
414         List<DatapathTypeEntry> datapathTypeEntries = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class)
415                 .getDatapathTypeEntry();
416         if (datapathTypeEntries == null) {
417             LOG.info("DPDK not supported on this node.");
418         } else {
419             for (DatapathTypeEntry dpTypeEntry : datapathTypeEntries) {
420                 Class<? extends DatapathTypeBase> dpType = dpTypeEntry.getDatapathType();
421                 String dpTypeStr = SouthboundConstants.DATAPATH_TYPE_MAP.get(dpType);
422                 LOG.info("dp type is {}", dpTypeStr);
423                 if (dpTypeStr.equals(NETDEV_DP_TYPE)) {
424                     LOG.info("Found a DPDK node; adding a corresponding netdev device");
425                     InstanceIdentifier<Node> bridgeIid = createInstanceIdentifier(connectionInfo,
426                             new OvsdbBridgeName(SouthboundITConstants.BRIDGE_NAME));
427                     NodeId bridgeNodeId = createManagedNodeId(bridgeIid);
428                     addBridge(connectionInfo, bridgeIid, SouthboundITConstants.BRIDGE_NAME, bridgeNodeId, false, null,
429                             true, dpType, null, null, null);
430
431                     // Verify that the device is netdev
432                     OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
433                     Assert.assertNotNull(bridge);
434                     Assert.assertEquals(dpType, bridge.getDatapathType());
435
436                     // Add dpdk port
437                     final String TEST_PORT_NAME = "testDPDKPort";
438                     OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
439                             createGenericDpdkOvsdbTerminationPointAugmentationBuilder(TEST_PORT_NAME);
440                     Assert.assertTrue(addTerminationPoint(bridgeNodeId, TEST_PORT_NAME, ovsdbTerminationBuilder));
441
442                     // Verify that DPDK port was created
443                     InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
444                     Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
445                             terminationPointIid);
446                     Assert.assertNotNull(terminationPointNode);
447
448                     // Verify that each termination point has DPDK ifType
449                     Class<? extends InterfaceTypeBase> dpdkIfType = SouthboundConstants.OVSDB_INTERFACE_TYPE_MAP
450                             .get("dpdk");
451                     List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
452                     for (TerminationPoint terminationPoint : terminationPoints) {
453                         OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation = terminationPoint
454                                 .getAugmentation(OvsdbTerminationPointAugmentation.class);
455                         if (ovsdbTerminationPointAugmentation.getName().equals(TEST_PORT_NAME)) {
456                             Class<? extends InterfaceTypeBase> opPort = ovsdbTerminationPointAugmentation
457                                     .getInterfaceType();
458                             Assert.assertEquals(dpdkIfType, opPort);
459                         }
460                     }
461                     Assert.assertTrue(deleteBridge(connectionInfo));
462                     break;
463                 }
464             }
465         }
466         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
467     }
468
469     @Test
470     public void testOvsdbNodeOvsVersion() throws InterruptedException {
471         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
472         Node ovsdbNode = connectOvsdbNode(connectionInfo);
473         OvsdbNodeAugmentation ovsdbNodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
474         Assert.assertNotNull(ovsdbNodeAugmentation);
475         assertNotNull(ovsdbNodeAugmentation.getOvsVersion());
476         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
477     }
478
479     @Test
480     public void testOpenVSwitchOtherConfig() throws InterruptedException {
481         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
482         Node ovsdbNode = connectOvsdbNode(connectionInfo);
483         OvsdbNodeAugmentation ovsdbNodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
484         Assert.assertNotNull(ovsdbNodeAugmentation);
485         List<OpenvswitchOtherConfigs> otherConfigsList = ovsdbNodeAugmentation.getOpenvswitchOtherConfigs();
486         if (otherConfigsList != null) {
487             for (OpenvswitchOtherConfigs otherConfig : otherConfigsList) {
488                 if (otherConfig.getOtherConfigKey().equals("local_ip")) {
489                     LOG.info("local_ip: {}", otherConfig.getOtherConfigValue());
490                     break;
491                 } else {
492                     LOG.info("other_config {}:{}", otherConfig.getOtherConfigKey(), otherConfig.getOtherConfigValue());
493                 }
494             }
495         } else {
496             LOG.info("other_config is not present");
497         }
498         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
499     }
500
501     @Test
502     public void testOvsdbBridgeControllerInfo() throws InterruptedException {
503         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
504         Node ovsdbNode = connectOvsdbNode(connectionInfo);
505         String controllerTarget = SouthboundUtil.getControllerTarget(ovsdbNode);
506         assertNotNull("Failed to get controller target", controllerTarget);
507         List<ControllerEntry> setControllerEntry = createControllerEntry(controllerTarget);
508         Uri setUri = new Uri(controllerTarget);
509         Assert.assertTrue(addBridge(connectionInfo, null, SouthboundITConstants.BRIDGE_NAME,null, true,
510                 SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null,
511                 setControllerEntry, null));
512         OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
513         Assert.assertNotNull("bridge was not found: " + SouthboundITConstants.BRIDGE_NAME,  bridge);
514         Assert.assertNotNull("ControllerEntry was not found: " + setControllerEntry.iterator().next(),
515                 bridge.getControllerEntry());
516         List<ControllerEntry> getControllerEntries = bridge.getControllerEntry();
517         for (ControllerEntry entry : getControllerEntries) {
518             if (entry.getTarget() != null) {
519                 Assert.assertEquals(setUri.toString(), entry.getTarget().toString());
520             }
521         }
522
523         Assert.assertTrue(deleteBridge(connectionInfo));
524         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
525     }
526
527     private List<ControllerEntry> createControllerEntry(String controllerTarget) {
528         List<ControllerEntry> controllerEntriesList = new ArrayList<>();
529         controllerEntriesList.add(new ControllerEntryBuilder()
530                 .setTarget(new Uri(controllerTarget))
531                 .build());
532         return controllerEntriesList;
533     }
534
535     private void setManagedBy(final OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder,
536                               final ConnectionInfo connectionInfo) {
537         InstanceIdentifier<Node> connectionNodePath = createInstanceIdentifier(connectionInfo);
538         ovsdbBridgeAugmentationBuilder.setManagedBy(new OvsdbNodeRef(connectionNodePath));
539     }
540
541     private List<ProtocolEntry> createMdsalProtocols() {
542         List<ProtocolEntry> protocolList = new ArrayList<>();
543         ImmutableBiMap<String, Class<? extends OvsdbBridgeProtocolBase>> mapper =
544                 SouthboundConstants.OVSDB_PROTOCOL_MAP.inverse();
545         protocolList.add(new ProtocolEntryBuilder().setProtocol(mapper.get("OpenFlow13")).build());
546         return protocolList;
547     }
548
549     private OvsdbTerminationPointAugmentationBuilder createGenericOvsdbTerminationPointAugmentationBuilder() {
550         OvsdbTerminationPointAugmentationBuilder ovsdbTerminationPointAugmentationBuilder =
551                 new OvsdbTerminationPointAugmentationBuilder();
552         ovsdbTerminationPointAugmentationBuilder.setInterfaceType(
553                 new InterfaceTypeEntryBuilder()
554                         .setInterfaceType(
555                                 SouthboundMapper.createInterfaceType("internal"))
556                         .build().getInterfaceType());
557         return ovsdbTerminationPointAugmentationBuilder;
558     }
559
560     private OvsdbTerminationPointAugmentationBuilder createGenericDpdkOvsdbTerminationPointAugmentationBuilder(
561             final String portName) {
562         OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
563                 createGenericOvsdbTerminationPointAugmentationBuilder();
564         ovsdbTerminationBuilder.setName(portName);
565         Class<? extends InterfaceTypeBase> ifType = SouthboundConstants.OVSDB_INTERFACE_TYPE_MAP
566                 .get("dpdk");
567         ovsdbTerminationBuilder.setInterfaceType(ifType);
568         return ovsdbTerminationBuilder;
569     }
570
571     private boolean addTerminationPoint(final NodeId bridgeNodeId, final String portName,
572                                         final OvsdbTerminationPointAugmentationBuilder
573                                                 ovsdbTerminationPointAugmentationBuilder)
574             throws InterruptedException {
575
576         InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(bridgeNodeId);
577         NodeBuilder portNodeBuilder = new NodeBuilder();
578         NodeId portNodeId = SouthboundMapper.createManagedNodeId(portIid);
579         portNodeBuilder.setNodeId(portNodeId);
580         TerminationPointBuilder entry = new TerminationPointBuilder();
581         entry.setKey(new TerminationPointKey(new TpId(portName)));
582         entry.addAugmentation(
583                 OvsdbTerminationPointAugmentation.class,
584                 ovsdbTerminationPointAugmentationBuilder.build());
585         portNodeBuilder.setTerminationPoint(Lists.newArrayList(entry.build()));
586         boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
587                 portIid, portNodeBuilder.build());
588         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
589         return result;
590     }
591
592     /*
593      * base method for adding test bridges.  Other helper methods used to create bridges should utilize this method.
594      *
595      * @param connectionInfo
596      * @param bridgeIid if passed null, one is created
597      * @param bridgeName cannot be null
598      * @param bridgeNodeId if passed null, one is created based on <code>bridgeIid</code>
599      * @param setProtocolEntries toggles whether default protocol entries are set for the bridge
600      * @param failMode toggles whether default fail mode is set for the bridge
601      * @param setManagedBy toggles whether to setManagedBy for the bridge
602      * @param dpType if passed null, this parameter is ignored
603      * @param externalIds if passed null, this parameter is ignored
604      * @param otherConfig if passed null, this parameter is ignored
605      * @return success of bridge addition
606      * @throws InterruptedException
607      */
608     private boolean addBridge(final ConnectionInfo connectionInfo, InstanceIdentifier<Node> bridgeIid,
609                               final String bridgeName, NodeId bridgeNodeId, final boolean setProtocolEntries,
610                               final Class<? extends OvsdbFailModeBase> failMode, final boolean setManagedBy,
611                               final Class<? extends DatapathTypeBase> dpType,
612                               final List<BridgeExternalIds> externalIds,
613                               final List<ControllerEntry> controllerEntries,
614                               final List<BridgeOtherConfigs> otherConfigs) throws InterruptedException {
615
616         NodeBuilder bridgeNodeBuilder = new NodeBuilder();
617         if (bridgeIid == null) {
618             bridgeIid = createInstanceIdentifier(connectionInfo, new OvsdbBridgeName(bridgeName));
619         }
620         if (bridgeNodeId == null) {
621             bridgeNodeId = SouthboundMapper.createManagedNodeId(bridgeIid);
622         }
623         bridgeNodeBuilder.setNodeId(bridgeNodeId);
624         OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
625         ovsdbBridgeAugmentationBuilder.setBridgeName(new OvsdbBridgeName(bridgeName));
626         if (setProtocolEntries) {
627             ovsdbBridgeAugmentationBuilder.setProtocolEntry(createMdsalProtocols());
628         }
629         if (failMode != null) {
630             ovsdbBridgeAugmentationBuilder.setFailMode(failMode);
631         }
632         if (setManagedBy) {
633             setManagedBy(ovsdbBridgeAugmentationBuilder, connectionInfo);
634         }
635         if (dpType != null) {
636             ovsdbBridgeAugmentationBuilder.setDatapathType(dpType);
637         }
638         if (externalIds != null) {
639             ovsdbBridgeAugmentationBuilder.setBridgeExternalIds(externalIds);
640         }
641         if (controllerEntries != null) {
642             ovsdbBridgeAugmentationBuilder.setControllerEntry(controllerEntries);
643         }
644         if (otherConfigs != null) {
645             ovsdbBridgeAugmentationBuilder.setBridgeOtherConfigs(otherConfigs);
646         }
647         bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, ovsdbBridgeAugmentationBuilder.build());
648         LOG.debug("Built with the intent to store bridge data {}",
649                 ovsdbBridgeAugmentationBuilder.toString());
650         boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
651                 bridgeIid, bridgeNodeBuilder.build());
652         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
653         return result;
654     }
655
656     private boolean addBridge(final ConnectionInfo connectionInfo, final String bridgeName)
657             throws InterruptedException {
658
659         return addBridge(connectionInfo, null, bridgeName, null, true,
660                 SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null);
661     }
662
663     private OvsdbBridgeAugmentation getBridge(ConnectionInfo connectionInfo) {
664         return getBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME);
665     }
666
667     /**
668      * Extract the <code>store</code> type data store contents for the particular bridge identified by
669      * <code>bridgeName</code>.
670      *
671      * @param connectionInfo the connection information
672      * @param bridgeName the bridge name
673      * @param store defined by the <code>LogicalDatastoreType</code> enumeration
674      * @return <code>store</code> type data store contents
675      */
676     private OvsdbBridgeAugmentation getBridge(ConnectionInfo connectionInfo, String bridgeName,
677                                               LogicalDatastoreType store) {
678         Node bridgeNode = getBridgeNode(connectionInfo, bridgeName, store);
679         Assert.assertNotNull(bridgeNode);
680         OvsdbBridgeAugmentation ovsdbBridgeAugmentation = bridgeNode.getAugmentation(OvsdbBridgeAugmentation.class);
681         Assert.assertNotNull(ovsdbBridgeAugmentation);
682         return ovsdbBridgeAugmentation;
683     }
684
685     /**
686      * extract the <code>LogicalDataStoreType.OPERATIONAL</code> type data store contents for the particular bridge
687      * identified by <code>bridgeName</code>
688      *
689      * @param connectionInfo the connection information
690      * @param bridgeName the bridge name
691      * @see <code>SouthboundIT.getBridge(ConnectionInfo, String, LogicalDatastoreType)</code>
692      * @return <code>LogicalDatastoreType.OPERATIONAL</code> type data store contents
693      */
694     private OvsdbBridgeAugmentation getBridge(ConnectionInfo connectionInfo, String bridgeName) {
695         return getBridge(connectionInfo, bridgeName, LogicalDatastoreType.OPERATIONAL);
696     }
697
698     /**
699      * Extract the node contents from <code>store</code> type data store for the
700      * bridge identified by <code>bridgeName</code>
701      *
702      * @param connectionInfo the connection information
703      * @param bridgeName the bridge name
704      * @param store defined by the <code>LogicalDatastoreType</code> enumeration
705      * @return <code>store</code> type data store contents
706      */
707     private Node getBridgeNode(ConnectionInfo connectionInfo, String bridgeName, LogicalDatastoreType store) {
708         InstanceIdentifier<Node> bridgeIid =
709                 createInstanceIdentifier(connectionInfo,
710                         new OvsdbBridgeName(bridgeName));
711         return mdsalUtils.read(store, bridgeIid);
712     }
713
714     /**
715      * Extract the node contents from <code>LogicalDataStoreType.OPERATIONAL</code> data store for the
716      * bridge identified by <code>bridgeName</code>
717      *
718      * @param connectionInfo the connection information
719      * @param bridgeName the bridge name
720      * @return <code>LogicalDatastoreType.OPERATIONAL</code> type data store contents
721      */
722     private Node getBridgeNode(ConnectionInfo connectionInfo, String bridgeName) {
723         return getBridgeNode(connectionInfo, bridgeName, LogicalDatastoreType.OPERATIONAL);
724     }
725
726     private boolean deleteBridge(ConnectionInfo connectionInfo) throws InterruptedException {
727         return deleteBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME);
728     }
729
730     private boolean deleteBridge(final ConnectionInfo connectionInfo, final String bridgeName)
731             throws InterruptedException {
732
733         boolean result = mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION,
734                 createInstanceIdentifier(connectionInfo,
735                         new OvsdbBridgeName(bridgeName)));
736         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
737         return result;
738     }
739
740     @Test
741     public void testAddDeleteBridge() throws InterruptedException {
742         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
743         connectOvsdbNode(connectionInfo);
744
745         Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
746         OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
747         Assert.assertNotNull(bridge);
748         LOG.info("bridge: {}", bridge);
749
750         Assert.assertTrue(deleteBridge(connectionInfo));
751
752         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
753     }
754
755     private InstanceIdentifier<Node> getTpIid(ConnectionInfo connectionInfo, OvsdbBridgeAugmentation bridge) {
756         return createInstanceIdentifier(connectionInfo,
757                 bridge.getBridgeName());
758     }
759
760     /**
761      * Extracts the <code>TerminationPointAugmentation</code> for the <code>index</code> <code>TerminationPoint</code>
762      * on <code>bridgeName</code>
763      *
764      * @param connectionInfo the connection information
765      * @param bridgeName the bridge name
766      * @param store defined by the <code>LogicalDatastoreType</code> enumeration
767      * @param index the index we're interested in
768      * @return the augmentation (or {@code null} if none)
769      */
770     private OvsdbTerminationPointAugmentation getOvsdbTerminationPointAugmentation(
771             ConnectionInfo connectionInfo, String bridgeName, LogicalDatastoreType store, int index ) {
772
773         List<TerminationPoint> tpList = getBridgeNode(connectionInfo, bridgeName, store).getTerminationPoint();
774         if (tpList == null) {
775             return null;
776         }
777         return tpList.get(index).getAugmentation(OvsdbTerminationPointAugmentation.class);
778     }
779
780     @Test
781     public void testCRDTerminationPointOfPort() throws InterruptedException {
782         final Long OFPORT_EXPECTED = 45002L;
783
784         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
785         connectOvsdbNode(connectionInfo);
786
787         // CREATE
788         Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
789         OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
790         Assert.assertNotNull(bridge);
791         LOG.info("bridge: {}", bridge);
792         NodeId nodeId = SouthboundMapper.createManagedNodeId(createInstanceIdentifier(
793                 connectionInfo, bridge.getBridgeName()));
794         OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
795                 createGenericOvsdbTerminationPointAugmentationBuilder();
796         String portName = "testOfPort";
797         ovsdbTerminationBuilder.setName(portName);
798
799         ovsdbTerminationBuilder.setOfport(OFPORT_EXPECTED);
800         Assert.assertTrue(addTerminationPoint(nodeId, portName, ovsdbTerminationBuilder));
801         InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
802         Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
803         Assert.assertNotNull(terminationPointNode);
804
805         // READ
806         List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
807         for (TerminationPoint terminationPoint : terminationPoints) {
808             OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
809                     terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
810             if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
811                 Long ofPort = ovsdbTerminationPointAugmentation.getOfport();
812                 // if ephemeral port 45002 is in use, ofPort is set to 1
813                 Assert.assertTrue(ofPort.equals(OFPORT_EXPECTED) || ofPort.equals(new Long(1)));
814                 LOG.info("ofPort: {}", ofPort);
815             }
816         }
817
818         // UPDATE- Not Applicable.  From the OpenVSwitch Documentation:
819         //   "A client should ideally set this column’s value in the same database transaction that it uses to create
820         //   the interface."
821
822         // DELETE
823         Assert.assertTrue(deleteBridge(connectionInfo));
824         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
825     }
826
827     @Test
828     public void testCRDTerminationPointOfPortRequest() throws InterruptedException {
829         final Long OFPORT_EXPECTED = 45008L;
830         final Long OFPORT_INPUT = 45008L;
831
832         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
833         connectOvsdbNode(connectionInfo);
834
835         // CREATE
836         Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
837         OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
838         Assert.assertNotNull(bridge);
839         NodeId nodeId = createManagedNodeId(createInstanceIdentifier(
840                 connectionInfo, bridge.getBridgeName()));
841         OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
842                 createGenericOvsdbTerminationPointAugmentationBuilder();
843         String portName = "testOfPortRequest";
844         ovsdbTerminationBuilder.setName(portName);
845         Integer ofPortRequestExpected = OFPORT_EXPECTED.intValue();
846         ovsdbTerminationBuilder.setOfport(OFPORT_INPUT);
847         ovsdbTerminationBuilder.setOfportRequest(ofPortRequestExpected);
848         Assert.assertTrue(addTerminationPoint(nodeId, portName, ovsdbTerminationBuilder));
849         InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
850         Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
851         Assert.assertNotNull(terminationPointNode);
852
853         // READ
854         List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
855         for (TerminationPoint terminationPoint : terminationPoints) {
856             OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
857                     terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
858             if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
859                 Long ofPort = ovsdbTerminationPointAugmentation.getOfport();
860                 // if ephemeral port 45008 is in use, ofPort is set to 1
861                 Assert.assertTrue(ofPort.equals(OFPORT_EXPECTED) || ofPort.equals(new Long(1)));
862                 LOG.info("ofPort: {}", ofPort);
863
864                 Integer ofPortRequest = ovsdbTerminationPointAugmentation.getOfportRequest();
865                 Assert.assertTrue(ofPortRequest.equals(ofPortRequestExpected));
866                 LOG.info("ofPortRequest: {}", ofPortRequest);
867             }
868         }
869
870         // UPDATE- Not Applicable.  From the OpenVSwitch documentation:
871         //   "A client should ideally set this column’s value in the same database transaction that it uses to create
872         //   the interface. "
873
874         // DELETE
875         Assert.assertTrue(deleteBridge(connectionInfo));
876         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
877     }
878
879     private interface KeyValueBuilder<T> {
880         T build(String testName, String key, String value);
881         T[] build(String testName, int count, String key, String value);
882         void reset();
883     }
884
885     /**
886      * Find the real type arguments for the given class (in its hierarchy), with at least {@code nb} arguments.
887      *
888      * @param clazz The class to start with.
889      * @param nb The minimum number of type arguments.
890      * @param <T> The type of the starting class.
891      * @return The matching type arguments (a {@link RuntimeException} is thrown if none match).
892      */
893     private static <T> Type[] findTypeArguments(final Class<T> clazz, final int nb) {
894         if (clazz == null || clazz.getSuperclass() == null) {
895             throw new RuntimeException("Missing type parameters");
896         }
897         Type superClassType = clazz.getGenericSuperclass();
898         if (superClassType instanceof ParameterizedType && ((ParameterizedType) superClassType)
899                 .getActualTypeArguments().length >= nb) {
900             return ((ParameterizedType) superClassType).getActualTypeArguments();
901         }
902         return findTypeArguments(clazz.getSuperclass(), nb);
903     }
904
905     private abstract static class BaseKeyValueBuilder<T> implements KeyValueBuilder<T> {
906         private static final int COUNTER_START = 0;
907         private int counter = COUNTER_START;
908         @SuppressWarnings("unchecked")
909         private final Class<T> builtClass = (Class<T>) findTypeArguments(getClass(), 1)[0];
910
911         protected abstract Builder<T> builder();
912
913         protected abstract void setKey(Builder<T> builder, String key);
914
915         protected abstract void setValue(Builder<T> builder, String value);
916
917         @Override
918         public final T build(final String testName, final String key, final String value) {
919             final Builder<T> builder = builder();
920             this.counter++;
921             if (key != null) {
922                 setKey(builder, String.format(FORMAT_STR, testName, key, this.counter));
923             }
924             if (value != null) {
925                 setValue(builder, String.format(FORMAT_STR, testName, value, this.counter));
926             }
927             return builder.build();
928         }
929
930         @SuppressWarnings("unchecked")
931         @Override
932         public final T[] build(final String testName, final int count, final String key, final String value) {
933             final T[] instances = (T[]) Array.newInstance(builtClass, count);
934             for (int idx = 0; idx < count; idx++) {
935                 try {
936                     instances[idx] = build(testName, key, value);
937                 } catch (ArrayStoreException e) {
938                     LOG.error("Error storing a value; we think we're managing {}", builtClass, e);
939                     throw e;
940                 }
941             }
942             return instances;
943         }
944
945         @Override
946         public final void reset() {
947             this.counter = COUNTER_START;
948         }
949     }
950
951     private static final class SouthboundPortExternalIdsBuilder extends BaseKeyValueBuilder<PortExternalIds> {
952         @Override
953         protected Builder<PortExternalIds> builder() {
954             return new PortExternalIdsBuilder();
955         }
956
957         @Override
958         protected void setKey(Builder<PortExternalIds> builder, String key) {
959             ((PortExternalIdsBuilder) builder).setExternalIdKey(key);
960         }
961
962         @Override
963         protected void setValue(Builder<PortExternalIds> builder, String value) {
964             ((PortExternalIdsBuilder) builder).setExternalIdValue(value);
965         }
966     }
967
968     private static final class SouthboundInterfaceExternalIdsBuilder extends BaseKeyValueBuilder<InterfaceExternalIds> {
969         @Override
970         protected Builder<InterfaceExternalIds> builder() {
971             return new InterfaceExternalIdsBuilder();
972         }
973
974         @Override
975         protected void setKey(Builder<InterfaceExternalIds> builder, String key) {
976             ((InterfaceExternalIdsBuilder) builder).setExternalIdKey(key);
977         }
978
979         @Override
980         protected void setValue(Builder<InterfaceExternalIds> builder, String value) {
981             ((InterfaceExternalIdsBuilder) builder).setExternalIdValue(value);
982         }
983     }
984
985     private static final class SouthboundOptionsBuilder extends BaseKeyValueBuilder<Options> {
986         @Override
987         protected Builder<Options> builder() {
988             return new OptionsBuilder();
989         }
990
991         @Override
992         protected void setKey(Builder<Options> builder, String key) {
993             ((OptionsBuilder) builder).setOption(key);
994         }
995
996         @Override
997         protected void setValue(Builder<Options> builder, String value) {
998             ((OptionsBuilder) builder).setValue(value);
999         }
1000     }
1001
1002     private static final class SouthboundInterfaceOtherConfigsBuilder extends BaseKeyValueBuilder<InterfaceOtherConfigs> {
1003         @Override
1004         protected Builder<InterfaceOtherConfigs> builder() {
1005             return new InterfaceOtherConfigsBuilder();
1006         }
1007
1008         @Override
1009         protected void setKey(Builder<InterfaceOtherConfigs> builder, String key) {
1010             ((InterfaceOtherConfigsBuilder) builder).setOtherConfigKey(key);
1011         }
1012
1013         @Override
1014         protected void setValue(Builder<InterfaceOtherConfigs> builder, String value) {
1015             ((InterfaceOtherConfigsBuilder) builder).setOtherConfigValue(value);
1016         }
1017     }
1018
1019     private static final class SouthboundPortOtherConfigsBuilder extends BaseKeyValueBuilder<PortOtherConfigs> {
1020         @Override
1021         protected Builder<PortOtherConfigs> builder() {
1022             return new PortOtherConfigsBuilder();
1023         }
1024
1025         @Override
1026         protected void setKey(Builder<PortOtherConfigs> builder, String key) {
1027             ((PortOtherConfigsBuilder) builder).setOtherConfigKey(key);
1028         }
1029
1030         @Override
1031         protected void setValue(Builder<PortOtherConfigs> builder, String value) {
1032             ((PortOtherConfigsBuilder) builder).setOtherConfigValue(value);
1033         }
1034     }
1035
1036     private static final class SouthboundBridgeOtherConfigsBuilder extends BaseKeyValueBuilder<BridgeOtherConfigs> {
1037         @Override
1038         protected Builder<BridgeOtherConfigs> builder() {
1039             return new BridgeOtherConfigsBuilder();
1040         }
1041
1042         @Override
1043         protected void setKey(Builder<BridgeOtherConfigs> builder, String key) {
1044             ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigKey(key);
1045         }
1046
1047         @Override
1048         protected void setValue(Builder<BridgeOtherConfigs> builder, String value) {
1049             ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigValue(value);
1050         }
1051     }
1052
1053     private static final class SouthboundBridgeExternalIdsBuilder extends BaseKeyValueBuilder<BridgeExternalIds> {
1054         @Override
1055         protected Builder<BridgeExternalIds> builder() {
1056             return new BridgeExternalIdsBuilder();
1057         }
1058
1059         @Override
1060         protected void setKey(Builder<BridgeExternalIds> builder, String key) {
1061             ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdKey(key);
1062         }
1063
1064         @Override
1065         protected void setValue(Builder<BridgeExternalIds> builder, String value) {
1066             ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdValue(value);
1067         }
1068     }
1069
1070     /*
1071      * Generates the test cases involved in testing key-value-based data.  See inline comments for descriptions of
1072      * the particular cases considered.
1073      */
1074     private static <T> List<SouthboundTestCase<T>> generateKeyValueTestCases(
1075             KeyValueBuilder<T> builder, String idKey, String idValue) {
1076         List<SouthboundTestCase<T>> testCases = new ArrayList<>();
1077
1078         final String GOOD_KEY = "GoodKey";
1079         final String GOOD_VALUE = "GoodValue";
1080         final String NO_VALUE_FOR_KEY = "NoValueForKey";
1081         final String NO_KEY_FOR_VALUE = "NoKeyForValue";
1082
1083         // Test Case 1:  TestOne
1084         // Test Type:    Positive
1085         // Description:  Create a termination point with one value
1086         // Expected:     A port is created with the single value specified below
1087         final String testOneName = "TestOne";
1088         testCases.add(new SouthboundTestCaseBuilder<T>()
1089                 .name(testOneName)
1090                 .input(builder.build(testOneName, idKey, idValue))
1091                 .expectInputAsOutput()
1092                 .build());
1093         builder.reset();
1094
1095         // Test Case 2:  TestFive
1096         // Test Type:    Positive
1097         // Description:  Create a termination point with multiple (five) values
1098         // Expected:     A port is created with the five values specified below
1099         final String testFiveName = "TestFive";
1100         testCases.add(new SouthboundTestCaseBuilder<T>()
1101                 .name(testFiveName)
1102                 .input(builder.build(testFiveName, 5, idKey, idValue))
1103                 .expectInputAsOutput()
1104                 .build());
1105         builder.reset();
1106
1107         // Test Case 3:  TestOneGoodOneMalformedValue
1108         // Test Type:    Negative
1109         // Description:
1110         //     One perfectly fine input
1111         //        (TestOneGoodOneMalformedValue_GoodKey_1,
1112         //        TestOneGoodOneMalformedValue_GoodValue_1)
1113         //     and one malformed input which only has key specified
1114         //        (TestOneGoodOneMalformedValue_NoValueForKey_2,
1115         //        UNSPECIFIED)
1116         // Expected:     A port is created without any values
1117         final String testOneGoodOneMalformedValueName = "TestOneGoodOneMalformedValue";
1118         testCases.add(new SouthboundTestCaseBuilder<T>()
1119                 .name(testOneGoodOneMalformedValueName)
1120                 .input(
1121                         builder.build(testOneGoodOneMalformedValueName, GOOD_KEY, GOOD_VALUE),
1122                         builder.build(testOneGoodOneMalformedValueName, NO_VALUE_FOR_KEY, null)
1123                 )
1124                 .expect()
1125                 .build());
1126         builder.reset();
1127
1128         // Test Case 4:  TestOneGoodOneMalformedKey
1129         // Test Type:    Negative
1130         // Description:
1131         //     One perfectly fine input
1132         //        (TestOneGoodOneMalformedKey_GoodKey_1,
1133         //        TestOneGoodOneMalformedKey_GoodValue_1)
1134         //     and one malformed input which only has value specified
1135         //        (UNSPECIFIED,
1136         //        TestOneGoodOneMalformedKey_NoKeyForValue_2)
1137         // Expected:     A port is created without any values
1138         final String testOneGoodOneMalformedKeyName = "TestOneGoodOneMalformedKey";
1139         testCases.add(new SouthboundTestCaseBuilder<T>()
1140                 .name(testOneGoodOneMalformedKeyName)
1141                 .input(
1142                         builder.build(testOneGoodOneMalformedKeyName, GOOD_KEY, GOOD_VALUE),
1143                         builder.build(testOneGoodOneMalformedKeyName, null, NO_KEY_FOR_VALUE)
1144                 )
1145                 .expect()
1146                 .build());
1147         builder.reset();
1148
1149         return testCases;
1150     }
1151
1152     /*
1153      * Generates the test cases involved in testing PortExternalIds.  See inline comments for descriptions of
1154      * the particular cases considered.
1155      */
1156     private List<SouthboundTestCase<PortExternalIds>> generatePortExternalIdsTestCases() {
1157         return generateKeyValueTestCases(new SouthboundPortExternalIdsBuilder(), "PortExternalIdKey",
1158                 "PortExternalIdValue");
1159     }
1160
1161     /*
1162      * Tests the CRUD operations for <code>Port</code>
1163      * <code>external_ids</code>.
1164      *
1165      * @see <code>SouthboundIT.generatePortExternalIdsTestCases()</code> for
1166      * specific test case information
1167      */
1168     @Test
1169     public void testCRUDTerminationPointPortExternalIds()
1170             throws InterruptedException, ExecutionException {
1171
1172         final String TEST_PREFIX = "CRUDTPPortExternalIds";
1173
1174         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1175         connectOvsdbNode(connectionInfo);
1176
1177         // updateFromTestCases represent the original test case value.
1178         // updateToTestCases represent the new value after the update has been
1179         // performed.
1180         List<SouthboundTestCase<PortExternalIds>> updateFromTestCases = generatePortExternalIdsTestCases();
1181         List<SouthboundTestCase<PortExternalIds>> updateToTestCases = generatePortExternalIdsTestCases();
1182         String testBridgeName;
1183         String testPortName;
1184
1185         int counter = 1;
1186         // multihreads the test using NUM_THREADS
1187         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1188         for (SouthboundTestCase<PortExternalIds> fromTestCase : updateFromTestCases) {
1189             for (SouthboundTestCase<PortExternalIds> toTestCase : updateToTestCases) {
1190                 testPortName = testBridgeName = String.format(FORMAT_STR,
1191                         TEST_PREFIX, toTestCase.name, counter);
1192                 counter += 1;
1193                 executor.submit(new TestCRUDTerminationPointRunnable<>(
1194                         new SouthboundTestHelper<PortExternalIds>() {
1195                             @Override
1196                             public List<PortExternalIds> readValues(
1197                                     OvsdbTerminationPointAugmentation augmentation) {
1198                                 return augmentation.getPortExternalIds();
1199                             }
1200
1201                             @Override
1202                             public void writeValues(
1203                                     OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
1204                                     List<PortExternalIds> updateFromInput) {
1205                                 augmentationBuilder.setPortExternalIds(updateFromInput);
1206                             }
1207                         },
1208                         connectionInfo, testBridgeName, testPortName,
1209                         fromTestCase.inputValues,
1210                         fromTestCase.expectedValues,
1211                         toTestCase.inputValues,
1212                         toTestCase.expectedValues));
1213             }
1214         }
1215         executor.shutdown();
1216         executor.awaitTermination(5, TimeUnit.MINUTES);
1217         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1218     }
1219
1220     /**
1221      * Southbound test helper. Classes implementing this interface are used to provide concrete access to the input and
1222      * output of the underlying augmentation for the type being managed.
1223      *
1224      * @param <T> The type of data used for the test case.
1225      */
1226     private interface SouthboundTestHelper<T> {
1227         /**
1228          * Read the values from the augmentation. These would usually be checked against the expected values provided
1229          * for the test case.
1230          *
1231          * @param augmentation The augmentation to read from.
1232          * @return The values read.
1233          */
1234         List<T> readValues(OvsdbTerminationPointAugmentation augmentation);
1235
1236         /**
1237          * Write the values to the augmentation (via its builder). This would usually be used to apply the input values
1238          * provided for the test case.
1239          *
1240          * @param augmentationBuilder The augmentation builder.
1241          * @param values The values to write.
1242          */
1243         void writeValues(OvsdbTerminationPointAugmentationBuilder augmentationBuilder, List<T> values);
1244     }
1245
1246     /**
1247      * <p>
1248      * Test runner used to apply a suite of create/read/update/delete tests. Each instance of a runner expects:
1249      * </p>
1250      * <ul>
1251      * <li>a helper used to manipulate the appropriate data structures in the termination point augmentation (see
1252      * {@link SouthboundTestHelper});</li>
1253      * <li>connection information for the southbound;</li>
1254      * <li>a name to use for the test bridge (this allows multiple tests to be conducted in parallel against different
1255      * bridges);</li>
1256      * <li>a name to use for the test port;</li>
1257      * <li>the initial input values to use for the termination point augmentation;</li>
1258      * <li>the initial expected values to check the augmentation against;</li>
1259      * <li>the target input values to update the terminal point to;</li>
1260      * <li>the target expected values to check the augmentation point against.</li>
1261      * </ul>
1262      * <p>The following tests are performed:</p>
1263      * <ol>
1264      * <li>the bridge is added;</li>
1265      * <li>the termination point is added, with the provided initial input values;</li>
1266      * <li>the termination point is read from the <em>configuration</em> data store, and checked against the provided
1267      * initial expected values;</li>
1268      * <li>the termination point is read from the <em>operational</em> data store, and checked against the provided
1269      * initial expected values;</li>
1270      * <li>the termination point is updated by merging the provided target input values;</li>
1271      * <li>the termination point is read from the <em>configuration</em> data store, and checked against the provided
1272      * initial <b>and</b> target expected values;</li>
1273      * <li>the termination point is read from the <em>operational</em> data store, and checked against the provided
1274      * initial <b>and</b> target expected values;</li>
1275      * <li>the bridge is deleted.</li>
1276      * </ol>
1277      *
1278      * @param <T> The type of data used for the test case.
1279      */
1280     private final class TestCRUDTerminationPointRunnable<T> implements Runnable {
1281         private final SouthboundTestHelper<T> helper;
1282         private final ConnectionInfo connectionInfo;
1283         private final String testBridgeName;
1284         private final String testPortName;
1285         private final List<T> updateFromInput;
1286         private final List<T> updateFromExpected;
1287         private final List<T> updateToInput;
1288         private final List<T> updateToExpected;
1289
1290         private TestCRUDTerminationPointRunnable(
1291                 SouthboundTestHelper<T> helper, ConnectionInfo connectionInfo, String testBridgeName,
1292                 String testPortName, List<T> updateFromInput, List<T> updateFromExpected, List<T> updateToInput,
1293                 List<T> updateToExpected) {
1294             this.helper = helper;
1295             this.connectionInfo = connectionInfo;
1296             this.testBridgeName = testBridgeName;
1297             this.testPortName = testPortName;
1298             this.updateFromInput = updateFromInput;
1299             this.updateFromExpected = updateFromExpected;
1300             this.updateToInput = updateToInput;
1301             this.updateToExpected = updateToExpected;
1302         }
1303
1304         @Override
1305         public void run() {
1306             try {
1307                 final int TERMINATION_POINT_TEST_INDEX = 0;
1308                 // CREATE: Create the test bridge
1309                 Assert.assertTrue(addBridge(connectionInfo, null,
1310                         testBridgeName, null, true,
1311                         SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
1312                         true, null, null, null, null));
1313                 NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
1314                         connectionInfo, new OvsdbBridgeName(testBridgeName)));
1315                 OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
1316                         createGenericOvsdbTerminationPointAugmentationBuilder();
1317                 tpCreateAugmentationBuilder.setName(testPortName);
1318                 helper.writeValues(tpCreateAugmentationBuilder, updateFromInput);
1319                 Assert.assertTrue(addTerminationPoint(testBridgeNodeId, testPortName, tpCreateAugmentationBuilder));
1320
1321                 // READ: Read the test port and ensure changes are propagated to the CONFIGURATION data store,
1322                 // then repeat for OPERATIONAL data store
1323                 OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
1324                         getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
1325                                 LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
1326                 List<T> updateFromConfiguration = null;
1327                 if (updateFromConfigurationTerminationPointAugmentation != null) {
1328                     updateFromConfiguration =
1329                         helper.readValues(updateFromConfigurationTerminationPointAugmentation);
1330                 }
1331                 if (updateFromConfiguration != null) {
1332                     Assert.assertTrue(updateFromConfiguration.containsAll(updateFromExpected));
1333                 }
1334                 OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
1335                         getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
1336                                 LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
1337                 List<T> updateFromOperational = null;
1338                 if (updateFromOperationalTerminationPointAugmentation != null) {
1339                     updateFromOperational = helper.readValues(updateFromOperationalTerminationPointAugmentation);
1340                 }
1341                 if (updateFromOperational != null) {
1342                     Assert.assertTrue(updateFromOperational.containsAll(updateFromExpected));
1343                 }
1344
1345                 // UPDATE:  update the external_ids
1346                 testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeName).getNodeId();
1347                 OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
1348                         new OvsdbTerminationPointAugmentationBuilder();
1349                 helper.writeValues(tpUpdateAugmentationBuilder, updateToInput);
1350                 InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
1351                 NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
1352                 NodeId portUpdateNodeId = createManagedNodeId(portIid);
1353                 portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
1354                 TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
1355                 tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testPortName)));
1356                 tpUpdateBuilder.addAugmentation(
1357                         OvsdbTerminationPointAugmentation.class,
1358                         tpUpdateAugmentationBuilder.build());
1359                 portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
1360                 boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
1361                         portIid, portUpdateNodeBuilder.build());
1362                 Thread.sleep(OVSDB_UPDATE_TIMEOUT);
1363                 Assert.assertTrue(result);
1364
1365                 // READ: the test port and ensure changes are propagated to the CONFIGURATION data store,
1366                 // then repeat for OPERATIONAL data store
1367                 OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
1368                         getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
1369                                 LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
1370                 List<T> updateToConfiguration = helper.readValues(updateToConfigurationTerminationPointAugmentation);
1371                 Assert.assertTrue(updateToConfiguration.containsAll(updateToExpected));
1372                 Assert.assertTrue(updateToConfiguration.containsAll(updateFromExpected));
1373                 OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
1374                         getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
1375                                 LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
1376                 List<T> updateToOperational = helper.readValues(updateToOperationalTerminationPointAugmentation);
1377                 Assert.assertTrue(updateToOperational.containsAll(updateToExpected));
1378                 Assert.assertTrue(updateToOperational.containsAll(updateFromExpected));
1379
1380                 // DELETE
1381                 Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
1382             } catch (InterruptedException e) {
1383                 LOG.error("Test interrupted", e);
1384             }
1385         }
1386     }
1387
1388
1389     /*
1390      * Generates the test cases involved in testing InterfaceExternalIds.  See inline comments for descriptions of
1391      * the particular cases considered.
1392      */
1393     private static List<SouthboundTestCase<InterfaceExternalIds>> generateInterfaceExternalIdsTestCases() {
1394         return generateKeyValueTestCases(new SouthboundInterfaceExternalIdsBuilder(), "IntExternalIdKey",
1395                 "IntExternalIdValue");
1396     }
1397
1398     /*
1399      * Tests the CRUD operations for <code>Interface</code> <code>external_ids</code>.
1400      *
1401      * @see <code>SouthboundIT.generateInterfaceExternalIdsTestCases()</code> for specific test case information
1402      */
1403     @Test
1404     public void testCRUDTerminationPointInterfaceExternalIds() throws InterruptedException, ExecutionException {
1405         final String TEST_PREFIX = "CRUDTPInterfaceExternalIds";
1406
1407         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1408         connectOvsdbNode(connectionInfo);
1409
1410         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
1411         // the update has been performed.
1412         List<SouthboundTestCase<InterfaceExternalIds>> updateFromTestCases = generateInterfaceExternalIdsTestCases();
1413         List<SouthboundTestCase<InterfaceExternalIds>> updateToTestCases = generateInterfaceExternalIdsTestCases();
1414         String testBridgeName;
1415         String testPortName;
1416
1417         int counter = 1;
1418         // multithreads the test using NUM_THREADS
1419         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1420         for (SouthboundTestCase<InterfaceExternalIds> fromTestCase : updateFromTestCases) {
1421             for (SouthboundTestCase<InterfaceExternalIds> toTestCase : updateToTestCases) {
1422                 testPortName = testBridgeName = String.format(FORMAT_STR,
1423                         TEST_PREFIX, toTestCase.name, counter);
1424                 counter += 1;
1425                 executor.submit(new TestCRUDTerminationPointRunnable<>(
1426                         new SouthboundTestHelper<InterfaceExternalIds>() {
1427                             @Override
1428                             public List<InterfaceExternalIds> readValues(
1429                                     OvsdbTerminationPointAugmentation augmentation) {
1430                                 return augmentation.getInterfaceExternalIds();
1431                             }
1432
1433                             @Override
1434                             public void writeValues(
1435                                     OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
1436                                     List<InterfaceExternalIds> values) {
1437                                 augmentationBuilder.setInterfaceExternalIds(values);
1438                             }
1439                         },
1440                         connectionInfo, testBridgeName, testPortName,
1441                         fromTestCase.inputValues,
1442                         fromTestCase.expectedValues,
1443                         toTestCase.inputValues,
1444                         toTestCase.expectedValues));
1445             }
1446         }
1447         executor.shutdown();
1448         executor.awaitTermination(5, TimeUnit.MINUTES);
1449         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1450     }
1451
1452     /*
1453      * Generates the test cases involved in testing TP Options.  See inline comments for descriptions of
1454      * the particular cases considered.
1455      */
1456     private List<SouthboundTestCase<Options>> generateTerminationPointOptionsTestCases() {
1457         return generateKeyValueTestCases(new SouthboundOptionsBuilder(), "TOPOptionsKey", "TPOptionsValue");
1458     }
1459
1460     /*
1461      * Tests the CRUD operations for <code>TerminationPoint</code> <code>options</code>.
1462      *
1463      * @see <code>SouthboundIT.generateTerminationPointOptions()</code> for specific test case information
1464      */
1465     @Test
1466     public void testCRUDTerminationPointOptions() throws InterruptedException {
1467         final String TEST_PREFIX = "CRUDTPOptions";
1468
1469         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1470         connectOvsdbNode(connectionInfo);
1471
1472         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
1473         // the update has been performed.
1474         List<SouthboundTestCase<Options>> updateFromTestCases = generateTerminationPointOptionsTestCases();
1475         List<SouthboundTestCase<Options>> updateToTestCases = generateTerminationPointOptionsTestCases();
1476         String testBridgeName;
1477         String testPortName;
1478
1479         int counter = 1;
1480         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1481         for (SouthboundTestCase<Options> fromTestCase : updateFromTestCases) {
1482             for (SouthboundTestCase<Options> toTestCase : updateToTestCases) {
1483                 testPortName = testBridgeName = String.format(FORMAT_STR,
1484                         TEST_PREFIX, toTestCase.name, counter);
1485                 counter += 1;
1486                 executor.submit(new TestCRUDTerminationPointRunnable<>(
1487                         new SouthboundTestHelper<Options>() {
1488                             @Override
1489                             public List<Options> readValues(
1490                                     OvsdbTerminationPointAugmentation augmentation) {
1491                                 return augmentation.getOptions();
1492                             }
1493
1494                             @Override
1495                             public void writeValues(
1496                                     OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
1497                                     List<Options> values) {
1498                                 augmentationBuilder.setOptions(values);
1499                             }
1500                         },
1501                         connectionInfo, testBridgeName, testPortName,
1502                         fromTestCase.inputValues,
1503                         fromTestCase.expectedValues,
1504                         toTestCase.inputValues,
1505                         toTestCase.expectedValues));
1506             }
1507         }
1508         executor.shutdown();
1509         executor.awaitTermination(5, TimeUnit.MINUTES);
1510         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1511     }
1512
1513     /*
1514      * Generates the test cases involved in testing Interface other_configs.  See inline comments for descriptions of
1515      * the particular cases considered.
1516      */
1517     private List<SouthboundTestCase<InterfaceOtherConfigs>> generateInterfaceOtherConfigsTestCases() {
1518         return generateKeyValueTestCases(new SouthboundInterfaceOtherConfigsBuilder(), "IntOtherConfigsKey",
1519                 "IntOtherConfigsValue");
1520     }
1521
1522     /*
1523      * Tests the CRUD operations for <code>Interface</code> <code>other_configs</code>.
1524      *
1525      * @see <code>SouthboundIT.generateInterfaceExternalIdsTestCases()</code> for specific test case information
1526      */
1527     @Test
1528     public void testCRUDTerminationPointInterfaceOtherConfigs() throws InterruptedException {
1529         final String TEST_PREFIX = "CRUDTPInterfaceOtherConfigs";
1530
1531         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1532         connectOvsdbNode(connectionInfo);
1533
1534         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
1535         // the update has been performed.
1536         List<SouthboundTestCase<InterfaceOtherConfigs>> updateFromTestCases = generateInterfaceOtherConfigsTestCases();
1537         List<SouthboundTestCase<InterfaceOtherConfigs>> updateToTestCases = generateInterfaceOtherConfigsTestCases();
1538         String testBridgeName;
1539         String testPortName;
1540
1541         int counter = 1;
1542         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1543         for (SouthboundTestCase<InterfaceOtherConfigs> fromTestCase : updateFromTestCases) {
1544             for (SouthboundTestCase<InterfaceOtherConfigs> toTestCase : updateToTestCases) {
1545                 testPortName = testBridgeName = String.format(FORMAT_STR,
1546                         TEST_PREFIX, toTestCase.name, counter);
1547                 counter += 1;
1548                 executor.submit(new TestCRUDTerminationPointRunnable<>(
1549                         new SouthboundTestHelper<InterfaceOtherConfigs>() {
1550                             @Override
1551                             public List<InterfaceOtherConfigs> readValues(
1552                                     OvsdbTerminationPointAugmentation augmentation) {
1553                                 return augmentation.getInterfaceOtherConfigs();
1554                             }
1555
1556                             @Override
1557                             public void writeValues(
1558                                     OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
1559                                     List<InterfaceOtherConfigs> values) {
1560                                 augmentationBuilder.setInterfaceOtherConfigs(values);
1561                             }
1562                         },
1563                         connectionInfo, testBridgeName, testPortName,
1564                         fromTestCase.inputValues,
1565                         fromTestCase.expectedValues,
1566                         toTestCase.inputValues,
1567                         toTestCase.expectedValues));
1568             }
1569         }
1570         executor.shutdown();
1571         executor.awaitTermination(5, TimeUnit.MINUTES);
1572         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1573     }
1574
1575     /*
1576      * Generates the test cases involved in testing Port other_configs.  See inline comments for descriptions of
1577      * the particular cases considered.
1578      */
1579     private List<SouthboundTestCase<PortOtherConfigs>> generatePortOtherConfigsTestCases() {
1580         return generateKeyValueTestCases(new SouthboundPortOtherConfigsBuilder(), "PortOtherConfigsKey",
1581                 "PortOtherConfigsValue");
1582     }
1583
1584     /*
1585      * Tests the CRUD operations for <code>Port</code> <code>other_configs</code>.
1586      *
1587      * @see <code>SouthboundIT.generatePortExternalIdsTestCases()</code> for specific test case information
1588      */
1589     @Test
1590     public void testCRUDTerminationPointPortOtherConfigs() throws InterruptedException {
1591         final String TEST_PREFIX = "CRUDTPPortOtherConfigs";
1592
1593         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1594         connectOvsdbNode(connectionInfo);
1595
1596         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
1597         // the update has been performed.
1598         List<SouthboundTestCase<PortOtherConfigs>> updateFromTestCases = generatePortOtherConfigsTestCases();
1599         List<SouthboundTestCase<PortOtherConfigs>> updateToTestCases = generatePortOtherConfigsTestCases();
1600         String testBridgeName;
1601         String testPortName;
1602
1603         int counter = 1;
1604         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1605         for (SouthboundTestCase<PortOtherConfigs> fromTestCase : updateFromTestCases) {
1606             for (SouthboundTestCase<PortOtherConfigs> toTestCase : updateToTestCases) {
1607                 testPortName = testBridgeName = String.format(FORMAT_STR,
1608                         TEST_PREFIX, toTestCase.name, counter);
1609                 counter += 1;
1610                 executor.submit(new TestCRUDTerminationPointRunnable<>(
1611                         new SouthboundTestHelper<PortOtherConfigs>() {
1612                             @Override
1613                             public List<PortOtherConfigs> readValues(
1614                                     OvsdbTerminationPointAugmentation augmentation) {
1615                                 return augmentation.getPortOtherConfigs();
1616                             }
1617
1618                             @Override
1619                             public void writeValues(
1620                                     OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
1621                                     List<PortOtherConfigs> values) {
1622                                 augmentationBuilder.setPortOtherConfigs(values);
1623                             }
1624                         },
1625                         connectionInfo, testBridgeName, testPortName,
1626                         fromTestCase.inputValues,
1627                         fromTestCase.expectedValues,
1628                         toTestCase.inputValues,
1629                         toTestCase.expectedValues));
1630             }
1631         }
1632         executor.shutdown();
1633         executor.awaitTermination(5, TimeUnit.MINUTES);
1634         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1635     }
1636
1637     @Test
1638     public void testCRUDTerminationPointVlan() throws InterruptedException {
1639         final Integer CREATED_VLAN_ID = 4000;
1640         final Integer UPDATED_VLAN_ID = 4001;
1641
1642         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1643         connectOvsdbNode(connectionInfo);
1644
1645         // CREATE
1646         Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
1647         OvsdbBridgeAugmentation bridge = getBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME);
1648         Assert.assertNotNull(bridge);
1649         NodeId nodeId = createManagedNodeId(createInstanceIdentifier(
1650                 connectionInfo, bridge.getBridgeName()));
1651         OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
1652                 createGenericOvsdbTerminationPointAugmentationBuilder();
1653         String portName = "testTerminationPointVlanId";
1654         ovsdbTerminationBuilder.setName(portName);
1655         ovsdbTerminationBuilder.setVlanTag(new VlanId(CREATED_VLAN_ID));
1656         Assert.assertTrue(addTerminationPoint(nodeId, portName, ovsdbTerminationBuilder));
1657         InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
1658         Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1659         Assert.assertNotNull(terminationPointNode);
1660
1661         // READ
1662         List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
1663         OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation;
1664         for (TerminationPoint terminationPoint : terminationPoints) {
1665             ovsdbTerminationPointAugmentation = terminationPoint.getAugmentation(
1666                     OvsdbTerminationPointAugmentation.class);
1667             if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1668                 VlanId actualVlanId = ovsdbTerminationPointAugmentation.getVlanTag();
1669                 Assert.assertNotNull(actualVlanId);
1670                 Integer actualVlanIdInt = actualVlanId.getValue();
1671                 Assert.assertEquals(CREATED_VLAN_ID, actualVlanIdInt);
1672             }
1673         }
1674
1675         // UPDATE
1676         NodeId testBridgeNodeId = getBridgeNode(connectionInfo, SouthboundITConstants.BRIDGE_NAME).getNodeId();
1677         OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
1678                 new OvsdbTerminationPointAugmentationBuilder();
1679         tpUpdateAugmentationBuilder.setVlanTag(new VlanId(UPDATED_VLAN_ID));
1680         InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
1681         NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
1682         NodeId portUpdateNodeId = createManagedNodeId(portIid);
1683         portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
1684         TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
1685         tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(portName)));
1686         tpUpdateBuilder.addAugmentation(
1687                 OvsdbTerminationPointAugmentation.class,
1688                 tpUpdateAugmentationBuilder.build());
1689         tpUpdateBuilder.setTpId(new TpId(portName));
1690         portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
1691         boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
1692                 portIid, portUpdateNodeBuilder.build());
1693         Assert.assertTrue(result);
1694         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
1695
1696         terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1697         terminationPoints = terminationPointNode.getTerminationPoint();
1698         for (TerminationPoint terminationPoint : terminationPoints) {
1699             ovsdbTerminationPointAugmentation = terminationPoint.getAugmentation(
1700                     OvsdbTerminationPointAugmentation.class);
1701             if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1702                 VlanId actualVlanId = ovsdbTerminationPointAugmentation.getVlanTag();
1703                 Assert.assertNotNull(actualVlanId);
1704                 Integer actualVlanIdInt = actualVlanId.getValue();
1705                 Assert.assertEquals(UPDATED_VLAN_ID, actualVlanIdInt);
1706             }
1707         }
1708
1709         // DELETE
1710         Assert.assertTrue(deleteBridge(connectionInfo));
1711         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1712     }
1713
1714     @Test
1715     public void testCRUDTerminationPointVlanModes() throws InterruptedException {
1716         final VlanMode UPDATED_VLAN_MODE = VlanMode.Access;
1717         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1718         connectOvsdbNode(connectionInfo);
1719         VlanMode []vlanModes = VlanMode.values();
1720         for (VlanMode vlanMode : vlanModes) {
1721             // CREATE
1722             Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
1723             OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
1724             Assert.assertNotNull(bridge);
1725             NodeId nodeId = createManagedNodeId(createInstanceIdentifier(
1726                     connectionInfo, bridge.getBridgeName()));
1727             OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
1728                     createGenericOvsdbTerminationPointAugmentationBuilder();
1729             String portName = "testTerminationPointVlanMode" + vlanMode.toString();
1730             ovsdbTerminationBuilder.setName(portName);
1731             ovsdbTerminationBuilder.setVlanMode(vlanMode);
1732             Assert.assertTrue(addTerminationPoint(nodeId, portName, ovsdbTerminationBuilder));
1733             InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
1734             Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1735             Assert.assertNotNull(terminationPointNode);
1736
1737             // READ
1738             List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
1739             for (TerminationPoint terminationPoint : terminationPoints) {
1740                 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
1741                         terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
1742                 if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1743                     //test
1744                     Assert.assertTrue(ovsdbTerminationPointAugmentation.getVlanMode().equals(vlanMode));
1745                 }
1746             }
1747
1748             // UPDATE
1749             NodeId testBridgeNodeId = getBridgeNode(connectionInfo, SouthboundITConstants.BRIDGE_NAME).getNodeId();
1750             OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
1751                     new OvsdbTerminationPointAugmentationBuilder();
1752             tpUpdateAugmentationBuilder.setVlanMode(UPDATED_VLAN_MODE);
1753             InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
1754             NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
1755             NodeId portUpdateNodeId = createManagedNodeId(portIid);
1756             portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
1757             TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
1758             tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(portName)));
1759             tpUpdateBuilder.addAugmentation(
1760                     OvsdbTerminationPointAugmentation.class,
1761                     tpUpdateAugmentationBuilder.build());
1762             tpUpdateBuilder.setTpId(new TpId(portName));
1763             portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
1764             boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
1765                     portIid, portUpdateNodeBuilder.build());
1766             Assert.assertTrue(result);
1767             Thread.sleep(OVSDB_UPDATE_TIMEOUT);
1768
1769             terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1770             terminationPoints = terminationPointNode.getTerminationPoint();
1771             for (TerminationPoint terminationPoint : terminationPoints) {
1772                 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
1773                         terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
1774                 if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1775                     //test
1776                     Assert.assertEquals(UPDATED_VLAN_MODE, ovsdbTerminationPointAugmentation.getVlanMode());
1777                 }
1778             }
1779
1780             // DELETE
1781             Assert.assertTrue(deleteBridge(connectionInfo));
1782         }
1783         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1784     }
1785
1786     private ArrayList<Set<Integer>> generateVlanSets() {
1787         ArrayList<Set<Integer>> vlanSets = new ArrayList<>();
1788
1789         Set<Integer> emptySet = new HashSet<>();
1790         vlanSets.add(emptySet);
1791
1792         Set<Integer> singleSet = new HashSet<>();
1793         Integer single = 2222;
1794         singleSet.add(single);
1795         vlanSets.add(singleSet);
1796
1797         Set<Integer> minMaxMiddleSet = new HashSet<>();
1798         Integer min = 0;
1799         minMaxMiddleSet.add(min);
1800         Integer max = 4095;
1801         minMaxMiddleSet.add(max);
1802         Integer minPlusOne = min + 1;
1803         minMaxMiddleSet.add(minPlusOne);
1804         Integer maxMinusOne = max - 1;
1805         minMaxMiddleSet.add(maxMinusOne);
1806         Integer middle = (max - min) / 2;
1807         minMaxMiddleSet.add(middle);
1808         vlanSets.add(minMaxMiddleSet);
1809
1810         return vlanSets;
1811     }
1812
1813     private List<Trunks> buildTrunkList(Set<Integer> trunkSet) {
1814         List<Trunks> trunkList = Lists.newArrayList();
1815         for (Integer trunk : trunkSet) {
1816             TrunksBuilder trunkBuilder = new TrunksBuilder();
1817             trunkBuilder.setTrunk(new VlanId(trunk));
1818             trunkList.add(trunkBuilder.build());
1819         }
1820         return trunkList;
1821     }
1822
1823     @Test
1824     public void testCRUDTerminationPointVlanTrunks() throws InterruptedException {
1825         final List<Trunks> UPDATED_TRUNKS = buildTrunkList(Sets.newHashSet(2011));
1826         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1827         connectOvsdbNode(connectionInfo);
1828         Iterable<Set<Integer>> vlanSets = generateVlanSets();
1829         int testCase = 0;
1830         for (Set<Integer> vlanSet : vlanSets) {
1831             ++testCase;
1832             // CREATE
1833             Assert.assertTrue(addBridge(connectionInfo, SouthboundITConstants.BRIDGE_NAME));
1834             OvsdbBridgeAugmentation bridge = getBridge(connectionInfo);
1835             Assert.assertNotNull(bridge);
1836             NodeId nodeId = createManagedNodeId(createInstanceIdentifier(
1837                     connectionInfo, bridge.getBridgeName()));
1838             OvsdbTerminationPointAugmentationBuilder ovsdbTerminationBuilder =
1839                     createGenericOvsdbTerminationPointAugmentationBuilder();
1840             String portName = "testTerminationPointVlanTrunks" + testCase;
1841             ovsdbTerminationBuilder.setName(portName);
1842             List<Trunks> trunks = buildTrunkList(vlanSet);
1843             ovsdbTerminationBuilder.setTrunks(trunks);
1844             Assert.assertTrue(addTerminationPoint(nodeId, portName, ovsdbTerminationBuilder));
1845             InstanceIdentifier<Node> terminationPointIid = getTpIid(connectionInfo, bridge);
1846             Node terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1847             Assert.assertNotNull(terminationPointNode);
1848
1849             // READ
1850             List<TerminationPoint> terminationPoints = terminationPointNode.getTerminationPoint();
1851             for (TerminationPoint terminationPoint : terminationPoints) {
1852                 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
1853                         terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
1854                 if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1855                     List<Trunks> actualTrunks = ovsdbTerminationPointAugmentation.getTrunks();
1856                     for (Trunks trunk : trunks) {
1857                         Assert.assertTrue(actualTrunks.contains(trunk));
1858                     }
1859                 }
1860             }
1861
1862
1863             // UPDATE
1864             NodeId testBridgeNodeId = getBridgeNode(connectionInfo, SouthboundITConstants.BRIDGE_NAME).getNodeId();
1865             OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
1866                     new OvsdbTerminationPointAugmentationBuilder();
1867             tpUpdateAugmentationBuilder.setTrunks(UPDATED_TRUNKS);
1868             InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
1869             NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
1870             NodeId portUpdateNodeId = createManagedNodeId(portIid);
1871             portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
1872             TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
1873             tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(portName)));
1874             tpUpdateBuilder.addAugmentation(
1875                     OvsdbTerminationPointAugmentation.class,
1876                     tpUpdateAugmentationBuilder.build());
1877             tpUpdateBuilder.setTpId(new TpId(portName));
1878             portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
1879             boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
1880                     portIid, portUpdateNodeBuilder.build());
1881             Assert.assertTrue(result);
1882             Thread.sleep(OVSDB_UPDATE_TIMEOUT);
1883
1884             terminationPointNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, terminationPointIid);
1885             terminationPoints = terminationPointNode.getTerminationPoint();
1886             for (TerminationPoint terminationPoint : terminationPoints) {
1887                 OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
1888                         terminationPoint.getAugmentation(OvsdbTerminationPointAugmentation.class);
1889                 if (ovsdbTerminationPointAugmentation.getName().equals(portName)) {
1890                     //test
1891                     Assert.assertEquals(UPDATED_TRUNKS, ovsdbTerminationPointAugmentation.getTrunks());
1892                 }
1893             }
1894
1895             // DELETE
1896             Assert.assertTrue(deleteBridge(connectionInfo));
1897         }
1898         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1899     }
1900
1901     @Test
1902     public void testGetOvsdbNodes() throws InterruptedException {
1903         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1904         connectOvsdbNode(connectionInfo);
1905         InstanceIdentifier<Topology> topologyPath = InstanceIdentifier
1906                 .create(NetworkTopology.class)
1907                 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID));
1908
1909         Topology topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, topologyPath);
1910         InstanceIdentifier<Node> expectedNodeIid = createInstanceIdentifier(connectionInfo);
1911         NodeId expectedNodeId = expectedNodeIid.firstKeyOf(Node.class, NodeKey.class).getNodeId();
1912         Node foundNode = null;
1913         Assert.assertNotNull("Expected to find topology: " + topologyPath, topology);
1914         Assert.assertNotNull("Expected to find some nodes" + topology.getNode());
1915         LOG.info("expectedNodeId: {}, getNode: {}", expectedNodeId, topology.getNode());
1916         for (Node node : topology.getNode()) {
1917             if (node.getNodeId().getValue().equals(expectedNodeId.getValue())) {
1918                 foundNode = node;
1919                 break;
1920             }
1921         }
1922         Assert.assertNotNull("Expected to find Node: " + expectedNodeId, foundNode);
1923         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1924     }
1925
1926     /*
1927      * Generates the test cases involved in testing BridgeOtherConfigs.  See inline comments for descriptions of
1928      * the particular cases considered.
1929      */
1930     private List<SouthboundTestCase<BridgeOtherConfigs>> generateBridgeOtherConfigsTestCases() {
1931         return generateKeyValueTestCases(new SouthboundBridgeOtherConfigsBuilder(), "BridgeOtherConfigsKey",
1932                 "BridgeOtherConfigsValue");
1933     }
1934
1935     /*
1936      * @see <code>SouthboundIT.testCRUDBridgeOtherConfigs()</code>
1937      * This is helper test method to compare a test "set" of BridgeExternalIds against an expected "set"
1938      */
1939     private void assertExpectedBridgeOtherConfigsExist( List<BridgeOtherConfigs> expected,
1940                                                         List<BridgeOtherConfigs> test ) {
1941
1942         if (expected != null) {
1943             for (BridgeOtherConfigs expectedOtherConfig : expected) {
1944                 Assert.assertTrue(test.contains(expectedOtherConfig));
1945             }
1946         }
1947     }
1948
1949     /*
1950      * @see <code>SouthboundIT.generateBridgeOtherConfigsTestCases()</code> for specific test case information.
1951      */
1952     @Test
1953     public void testCRUDBridgeOtherConfigs() throws InterruptedException {
1954         final String TEST_BRIDGE_PREFIX = "CRUDBridgeOtherConfigs";
1955         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
1956         connectOvsdbNode(connectionInfo);
1957         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
1958         // the update has been performed.
1959         List<SouthboundTestCase<BridgeOtherConfigs>> updateFromTestCases = generateBridgeOtherConfigsTestCases();
1960         List<SouthboundTestCase<BridgeOtherConfigs>> updateToTestCases = generateBridgeOtherConfigsTestCases();
1961         String testBridgeName;
1962
1963         int counter = 1;
1964         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
1965         for (SouthboundTestCase<BridgeOtherConfigs> fromTestCase : updateFromTestCases) {
1966             for (SouthboundTestCase<BridgeOtherConfigs> toTestCase : updateToTestCases) {
1967                 testBridgeName = String.format(FORMAT_STR, TEST_BRIDGE_PREFIX, toTestCase.name, counter);
1968                 counter += 1;
1969                 TestCRUDBridgeOtherConfigsRunnable testRunnable =
1970                         new TestCRUDBridgeOtherConfigsRunnable(
1971                                 connectionInfo, testBridgeName,
1972                                 fromTestCase.inputValues,
1973                                 fromTestCase.expectedValues,
1974                                 toTestCase.inputValues,
1975                                 toTestCase.expectedValues);
1976                 executor.submit(testRunnable);
1977             }
1978         }
1979         executor.shutdown();
1980         executor.awaitTermination(5, TimeUnit.MINUTES);
1981
1982         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
1983     }
1984
1985     class TestCRUDBridgeOtherConfigsRunnable implements Runnable {
1986
1987         ConnectionInfo connectionInfo;
1988         String testBridgeName;
1989         List<BridgeOtherConfigs> updateFromInputOtherConfigs;
1990         List<BridgeOtherConfigs> updateFromExpectedOtherConfigs;
1991         List<BridgeOtherConfigs> updateToInputOtherConfigs;
1992         List<BridgeOtherConfigs> updateToExpectedOtherConfigs;
1993
1994         TestCRUDBridgeOtherConfigsRunnable(
1995                 ConnectionInfo connectionInfo, String testBridgeName,
1996                 List<BridgeOtherConfigs> updateFromInputOtherConfigs,
1997                 List<BridgeOtherConfigs> updateFromExpectedOtherConfigs,
1998                 List<BridgeOtherConfigs> updateToInputOtherConfigs,
1999                 List<BridgeOtherConfigs> updateToExpectedOtherConfigs) {
2000
2001             this.connectionInfo = connectionInfo;
2002             this.testBridgeName = testBridgeName;
2003             this.updateFromInputOtherConfigs = updateFromInputOtherConfigs;
2004             this.updateFromExpectedOtherConfigs = updateFromExpectedOtherConfigs;
2005             this.updateToInputOtherConfigs = updateToInputOtherConfigs;
2006             this.updateToExpectedOtherConfigs = updateToExpectedOtherConfigs;
2007         }
2008
2009         @Override
2010         public void run() {
2011             try {
2012                 test();
2013             } catch (InterruptedException e) {
2014                 e.printStackTrace();
2015             }
2016         }
2017
2018         public void test() throws InterruptedException {
2019             // CREATE: Create the test bridge
2020             boolean bridgeAdded = addBridge(connectionInfo, null,
2021                     testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
2022                     true, null, null, null, updateFromInputOtherConfigs);
2023             Assert.assertTrue(bridgeAdded);
2024
2025             // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
2026             // then repeat for OPERATIONAL data store
2027             List<BridgeOtherConfigs> updateFromConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
2028                     LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
2029             assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
2030                     updateFromConfigurationOtherConfigs);
2031             List<BridgeOtherConfigs> updateFromOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName)
2032                     .getBridgeOtherConfigs();
2033             assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
2034                     updateFromOperationalOtherConfigs);
2035
2036             // UPDATE:  update the external_ids
2037             OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
2038             bridgeAugmentationBuilder.setBridgeOtherConfigs(updateToInputOtherConfigs);
2039             InstanceIdentifier<Node> bridgeIid =
2040                     createInstanceIdentifier(connectionInfo,
2041                             new OvsdbBridgeName(testBridgeName));
2042             NodeBuilder bridgeNodeBuilder = new NodeBuilder();
2043             Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
2044             bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
2045             bridgeNodeBuilder.setKey(bridgeNode.getKey());
2046             bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
2047             boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
2048                     bridgeNodeBuilder.build());
2049             Thread.sleep(OVSDB_UPDATE_TIMEOUT);
2050             Assert.assertTrue(result);
2051
2052             // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
2053             // then repeat for OPERATIONAL data store
2054             List<BridgeOtherConfigs> updateToConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
2055                     LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
2056             assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs, updateToConfigurationOtherConfigs);
2057             assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
2058                     updateToConfigurationOtherConfigs);
2059             List<BridgeOtherConfigs> updateToOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName)
2060                     .getBridgeOtherConfigs();
2061             if (updateFromExpectedOtherConfigs != null) {
2062                 assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs,
2063                         updateToOperationalOtherConfigs);
2064                 assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
2065                         updateToOperationalOtherConfigs);
2066             }
2067
2068             // DELETE
2069             Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
2070         }
2071     }
2072
2073     /*
2074      * Generates the test cases involved in testing BridgeExternalIds.  See inline comments for descriptions of
2075      * the particular cases considered.
2076      */
2077     private List<SouthboundTestCase<BridgeExternalIds>> generateBridgeExternalIdsTestCases() {
2078         return generateKeyValueTestCases(new SouthboundBridgeExternalIdsBuilder(), "BridgeExternalIdsKey",
2079                 "BridgeExternalIdsValue");
2080     }
2081
2082     /*
2083      * @see <code>SouthboundIT.testCRUDBridgeExternalIds()</code>
2084      * This is helper test method to compare a test "set" of BridgeExternalIds against an expected "set"
2085      */
2086     private void assertExpectedBridgeExternalIdsExist( List<BridgeExternalIds> expected,
2087                                                        List<BridgeExternalIds> test ) {
2088
2089         if (expected != null) {
2090             for (BridgeExternalIds expectedExternalId : expected) {
2091                 Assert.assertTrue(test.contains(expectedExternalId));
2092             }
2093         }
2094     }
2095
2096     /*
2097      * @see <code>SouthboundIT.generateBridgeExternalIdsTestCases()</code> for specific test case information
2098      */
2099     @Test
2100     public void testCRUDBridgeExternalIds() throws InterruptedException {
2101         final String TEST_BRIDGE_PREFIX = "CRUDBridgeExternalIds";
2102         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
2103         connectOvsdbNode(connectionInfo);
2104         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
2105         // the update has been performed.
2106         List<SouthboundTestCase<BridgeExternalIds>> updateFromTestCases = generateBridgeExternalIdsTestCases();
2107         List<SouthboundTestCase<BridgeExternalIds>> updateToTestCases = generateBridgeExternalIdsTestCases();
2108         String testBridgeName;
2109
2110         int counter = 1;
2111         ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
2112         for (SouthboundTestCase<BridgeExternalIds> fromTestCase : updateFromTestCases) {
2113             for (SouthboundTestCase<BridgeExternalIds> toTestCase : updateToTestCases) {
2114                 testBridgeName = String.format(FORMAT_STR, TEST_BRIDGE_PREFIX, toTestCase.name, counter);
2115                 counter += 1;
2116                 TestCRUDBridgeExternalIdsRunnable testRunnable =
2117                         new TestCRUDBridgeExternalIdsRunnable(
2118                                 connectionInfo, testBridgeName,
2119                                 fromTestCase.inputValues,
2120                                 fromTestCase.expectedValues,
2121                                 toTestCase.inputValues,
2122                                 toTestCase.expectedValues);
2123                 executor.submit(testRunnable);
2124             }
2125         }
2126         executor.shutdown();
2127         executor.awaitTermination(5, TimeUnit.MINUTES);
2128
2129         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
2130     }
2131
2132     class TestCRUDBridgeExternalIdsRunnable implements Runnable {
2133         ConnectionInfo connectionInfo;
2134         String testBridgeName;
2135         List<BridgeExternalIds> updateFromInputExternalIds;
2136         List<BridgeExternalIds> updateFromExpectedExternalIds;
2137         List<BridgeExternalIds> updateToInputExternalIds;
2138         List<BridgeExternalIds> updateToExpectedExternalIds;
2139
2140         TestCRUDBridgeExternalIdsRunnable(
2141                 ConnectionInfo connectionInfo, String testBridgeName,
2142                 List<BridgeExternalIds> updateFromInputExternalIds,
2143                 List<BridgeExternalIds> updateFromExpectedExternalIds,
2144                 List<BridgeExternalIds> updateToInputExternalIds,
2145                 List<BridgeExternalIds> updateToExpectedExternalIds) {
2146
2147             this.connectionInfo = connectionInfo;
2148             this.testBridgeName = testBridgeName;
2149             this.updateFromInputExternalIds = updateFromInputExternalIds;
2150             this.updateFromExpectedExternalIds = updateFromExpectedExternalIds;
2151             this.updateToInputExternalIds = updateToInputExternalIds;
2152             this.updateToExpectedExternalIds = updateToExpectedExternalIds;
2153         }
2154
2155         @Override
2156         public void run() {
2157             try {
2158                 test();
2159             } catch (InterruptedException e) {
2160                 e.printStackTrace();
2161             }
2162         }
2163
2164         public void test() throws InterruptedException {
2165             // CREATE: Create the test bridge
2166             boolean bridgeAdded = addBridge(connectionInfo, null,
2167                     testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
2168                     true, null, updateFromInputExternalIds, null, null);
2169             Assert.assertTrue(bridgeAdded);
2170
2171             // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
2172             // then repeat for OPERATIONAL data store
2173             List<BridgeExternalIds> updateFromConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
2174                     LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
2175             assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromConfigurationExternalIds);
2176             List<BridgeExternalIds> updateFromOperationalExternalIds = getBridge(connectionInfo, testBridgeName)
2177                     .getBridgeExternalIds();
2178             assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromOperationalExternalIds);
2179
2180             // UPDATE:  update the external_ids
2181             OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
2182             bridgeAugmentationBuilder.setBridgeExternalIds(updateToInputExternalIds);
2183             InstanceIdentifier<Node> bridgeIid =
2184                     createInstanceIdentifier(connectionInfo,
2185                             new OvsdbBridgeName(testBridgeName));
2186             NodeBuilder bridgeNodeBuilder = new NodeBuilder();
2187             Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
2188             bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
2189             bridgeNodeBuilder.setKey(bridgeNode.getKey());
2190             bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
2191             boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
2192                     bridgeNodeBuilder.build());
2193             Thread.sleep(OVSDB_UPDATE_TIMEOUT);
2194             Assert.assertTrue(result);
2195
2196             // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
2197             // then repeat for OPERATIONAL data store
2198             List<BridgeExternalIds> updateToConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
2199                     LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
2200             assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToConfigurationExternalIds);
2201             assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToConfigurationExternalIds);
2202             List<BridgeExternalIds> updateToOperationalExternalIds = getBridge(connectionInfo, testBridgeName)
2203                     .getBridgeExternalIds();
2204             if (updateFromExpectedExternalIds != null) {
2205                 assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToOperationalExternalIds);
2206                 assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToOperationalExternalIds);
2207             }
2208
2209             // DELETE
2210             Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
2211         }
2212     }
2213
2214     public static InstanceIdentifier<Node> createInstanceIdentifier(ConnectionInfo key,OvsdbBridgeName bridgeName) {
2215         return SouthboundMapper.createInstanceIdentifier(createManagedNodeId(key, bridgeName));
2216     }
2217
2218     public static NodeId createManagedNodeId(ConnectionInfo key, OvsdbBridgeName bridgeName) {
2219         return createManagedNodeId(key.getRemoteIp(), key.getRemotePort(), bridgeName);
2220     }
2221
2222     public static NodeId createManagedNodeId(IpAddress ip, PortNumber port, OvsdbBridgeName bridgeName) {
2223         return new NodeId(createNodeId(ip,port).getValue()
2224                 + "/" + SouthboundConstants.BRIDGE_URI_PREFIX + "/" + bridgeName.getValue());
2225     }
2226
2227     public static NodeId createNodeId(IpAddress ip, PortNumber port) {
2228         String uriString = SouthboundConstants.OVSDB_URI_PREFIX + "://"
2229                 + new String(ip.getValue()) + ":" + port.getValue();
2230         Uri uri = new Uri(uriString);
2231         return new NodeId(uri);
2232     }
2233
2234     public static NodeKey createNodeKey(IpAddress ip, PortNumber port) {
2235         return new NodeKey(createNodeId(ip,port));
2236     }
2237
2238     public static Node createNode(ConnectionInfo key) {
2239         NodeBuilder nodeBuilder = new NodeBuilder();
2240         nodeBuilder.setNodeId(createNodeId(key.getRemoteIp(),key.getRemotePort()));
2241         nodeBuilder.addAugmentation(OvsdbNodeAugmentation.class, createOvsdbAugmentation(key));
2242         return nodeBuilder.build();
2243     }
2244
2245     public static OvsdbNodeAugmentation createOvsdbAugmentation(ConnectionInfo key) {
2246         OvsdbNodeAugmentationBuilder ovsdbNodeBuilder = new OvsdbNodeAugmentationBuilder();
2247         ovsdbNodeBuilder.setConnectionInfo(key);
2248         return ovsdbNodeBuilder.build();
2249     }
2250
2251     public static NodeId createManagedNodeId(InstanceIdentifier<Node> iid) {
2252         NodeKey nodeKey = iid.firstKeyOf(Node.class, NodeKey.class);
2253         return nodeKey.getNodeId();
2254     }
2255
2256     /**
2257      * <p>
2258      * Representation of a southbound test case. Each test case has a name, a list of input values and a list of
2259      * expected values. The input values are provided to the augmentation builder, and the expected values are checked
2260      * against the output of the resulting augmentation.
2261      * </p>
2262      * <p>
2263      * Instances of this class are immutable.
2264      * </p>
2265      *
2266      * @param <T> The type of data used for the test case.
2267      */
2268     private static final class SouthboundTestCase<T> {
2269         private final String name;
2270         private final List<T> inputValues;
2271         private final List<T> expectedValues;
2272
2273         /**
2274          * Creates an instance of a southbound test case.
2275          *
2276          * @param name The test case's name.
2277          * @param inputValues The input values (provided as input to the underlying augmentation builder).
2278          * @param expectedValues The expected values (checked against the output of the underlying augmentation).
2279          */
2280         public SouthboundTestCase(
2281                 final String name, final List<T> inputValues, final List<T> expectedValues) {
2282             this.name = name;
2283             this.inputValues = inputValues;
2284             this.expectedValues = expectedValues;
2285         }
2286     }
2287
2288     /**
2289      * Southbound test case builder.
2290      *
2291      * @param <T> The type of data used for the test case.
2292      */
2293     private static final class SouthboundTestCaseBuilder<T> {
2294         private String name;
2295         private List<T> inputValues;
2296         private List<T> expectedValues;
2297
2298         /**
2299          * Creates a builder. Builders may be reused, the generated immutable instances are independent of the
2300          * builders. There are no default values.
2301          */
2302         public SouthboundTestCaseBuilder() {
2303             // Nothing to do
2304         }
2305
2306         /**
2307          * Sets the test case's name.
2308          *
2309          * @param name The test case's name.
2310          * @return The builder.
2311          */
2312         public SouthboundTestCaseBuilder<T> name(final String name) {
2313             this.name = name;
2314             return this;
2315         }
2316
2317         /**
2318          * Sets the input values.
2319          *
2320          * @param inputValues The input values.
2321          * @return The builder.
2322          */
2323         @SafeVarargs
2324         public final SouthboundTestCaseBuilder<T> input(final T... inputValues) {
2325             this.inputValues = Lists.newArrayList(inputValues);
2326             return this;
2327         }
2328
2329         /**
2330          * Sets the expected output values.
2331          *
2332          * @param expectedValues The expected output values.
2333          * @return The builder.
2334          */
2335         @SafeVarargs
2336         public final SouthboundTestCaseBuilder<T> expect(final T... expectedValues) {
2337             this.expectedValues = Lists.newArrayList(expectedValues);
2338             return this;
2339         }
2340
2341         /**
2342          * Indicates that the provided input values should be expected as output values.
2343          *
2344          * @return The builder.
2345          */
2346         public SouthboundTestCaseBuilder<T> expectInputAsOutput() {
2347             this.expectedValues = this.inputValues;
2348             return this;
2349         }
2350
2351         /**
2352          * Builds an immutable instance representing the test case.
2353          *
2354          * @return The test case.
2355          */
2356         @SuppressWarnings("unchecked")
2357         public SouthboundTestCase<T> build() {
2358             return new SouthboundTestCase<>(name, inputValues, expectedValues);
2359         }
2360     }
2361 }