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