2 * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.ovsdb.hwvtepsouthbound.it;
10 import static org.junit.Assert.fail;
11 import static org.ops4j.pax.exam.CoreOptions.composite;
12 import static org.ops4j.pax.exam.CoreOptions.maven;
13 import static org.ops4j.pax.exam.CoreOptions.vmOption;
14 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
15 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
17 import java.lang.annotation.Annotation;
18 import java.lang.reflect.Method;
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Properties;
27 import javax.annotation.Nullable;
28 import javax.inject.Inject;
30 import org.junit.After;
31 import org.junit.Assert;
32 import org.junit.Before;
33 import org.junit.Ignore;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
38 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
39 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
40 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
41 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
42 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
43 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundConstants;
44 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundMapper;
45 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundProvider;
46 import org.opendaylight.ovsdb.utils.hwvtepsouthbound.utils.HwvtepSouthboundUtils;
47 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalRef;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentationBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfoBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.ManagementIps;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.Tunnels;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.ops4j.pax.exam.Configuration;
67 import org.ops4j.pax.exam.Option;
68 import org.ops4j.pax.exam.junit.PaxExam;
69 import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
70 import org.ops4j.pax.exam.options.MavenUrlReference;
71 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
72 import org.ops4j.pax.exam.spi.reactors.PerClass;
73 import org.osgi.framework.BundleContext;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
77 @RunWith(PaxExam.class)
78 @ExamReactorStrategy(PerClass.class)
79 public class HwvtepSouthboundIT extends AbstractMdsalTestBase {
80 private static final Logger LOG = LoggerFactory.getLogger(HwvtepSouthboundIT.class);
84 public static final String ORG_OPS4J_PAX_LOGGING_CFG = "etc/org.ops4j.pax.logging.cfg";
85 public static final String CUSTOM_PROPERTIES = "etc/custom.properties";
86 public static final String SERVER_IPADDRESS = "ovsdbserver.ipaddress";
87 public static final String DEFAULT_SERVER_IPADDRESS = "127.0.0.1";
88 public static final String SERVER_PORT = "ovsdbserver.port";
89 public static final String DEFAULT_SERVER_PORT = "6640";
90 public static final String CONNECTION_TYPE = "ovsdbserver.connection";
91 public static final String CONNECTION_TYPE_ACTIVE = "active";
92 public static final String CONNECTION_TYPE_PASSIVE = "passive";
93 public static final int CONNECTION_INIT_TIMEOUT = 10000;
94 public static final String OPENFLOW_CONNECTION_PROTOCOL = "tcp";
95 private static final String PS_NAME = "ps0";
97 private static final int OVSDB_UPDATE_TIMEOUT = 1000;
98 private static final int OVSDB_ROUNDTRIP_TIMEOUT = 10000;
100 private static MdsalUtils mdsalUtils = null;
101 private static boolean setup = false;
102 private static int testMethodsRemaining;
103 private static String addressStr;
104 private static int portNumber;
105 private static String connectionType;
106 private static Node hwvtepNode;
109 private BundleContext bundleContext;
111 private static final NotifyingDataChangeListener OPERATIONAL_LISTENER =
112 new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL);
114 private static class NotifyingDataChangeListener implements DataTreeChangeListener<Node> {
115 private final LogicalDatastoreType type;
116 private final Set<InstanceIdentifier<Node>> createdNodes = new HashSet<>();
117 private final Set<InstanceIdentifier<Node>> removedNodes = new HashSet<>();
118 private final Set<InstanceIdentifier<Node>> updatedNodes = new HashSet<>();
120 private NotifyingDataChangeListener(LogicalDatastoreType type) {
125 public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
126 for (DataTreeModification<Node> change : changes) {
127 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
128 final DataObjectModification<Node> mod = change.getRootNode();
129 switch (mod.getModificationType()) {
131 removedNodes.add(key);
133 case SUBTREE_MODIFIED:
134 updatedNodes.add(key);
137 if (mod.getDataBefore() == null) {
138 LOG.trace("Data added: {}", mod.getDataAfter());
139 createdNodes.add(key);
141 updatedNodes.add(key);
145 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
150 public boolean isCreated(InstanceIdentifier<Node> iid) {
151 return createdNodes.remove(iid);
154 public boolean isRemoved(InstanceIdentifier<Node> iid) {
155 return removedNodes.remove(iid);
158 public boolean isUpdated(InstanceIdentifier<Node> iid) {
159 return updatedNodes.remove(iid);
164 public Option[] config() {
165 Option[] options = super.config();
166 Option[] propertyOptions = getPropertiesOptions();
167 Option[] otherOptions = getOtherOptions();
168 Option[] combinedOptions = new Option[options.length + propertyOptions.length + otherOptions.length];
169 System.arraycopy(options, 0, combinedOptions, 0, options.length);
170 System.arraycopy(propertyOptions, 0, combinedOptions, options.length, propertyOptions.length);
171 System.arraycopy(otherOptions, 0, combinedOptions, options.length + propertyOptions.length,
172 otherOptions.length);
173 return combinedOptions;
176 private Option[] getOtherOptions() {
177 return new Option[] {
178 vmOption("-javaagent:../jars/org.jacoco.agent.jar=destfile=../../jacoco-it.exec"),
184 public String getModuleName() {
185 return "hwvtepsouthbound";
189 public String getInstanceName() {
190 return "hwvtepsouthbound-default";
194 public MavenUrlReference getFeatureRepo() {
196 .groupId("org.opendaylight.ovsdb")
197 .artifactId("hwvtepsouthbound-features")
198 .classifier("features")
200 .versionAsInProject();
204 public String getFeatureName() {
205 return "odl-ovsdb-hwvtepsouthbound-test";
209 public Option getLoggingOption() {
210 Option option = editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
211 logConfiguration(HwvtepSouthboundIT.class),
212 LogLevel.INFO.name());
213 option = composite(option, super.getLoggingOption());
218 public String getKarafDistro() {
220 .groupId("org.opendaylight.ovsdb")
221 .artifactId("hwvtepsouthbound-karaf")
222 .versionAsInProject()
227 protected String usage() {
228 return "Integration Test needs a valid connection configuration as follows :\n"
229 + "active connection : mvn -Dovsdbserver.ipaddress=x.x.x.x -Dovsdbserver.port=yyyy verify\n"
230 + "passive connection : mvn -Dovsdbserver.connection=passive verify\n";
233 private Option[] getPropertiesOptions() {
234 Properties props = new Properties(System.getProperties());
235 String addressStr = props.getProperty(SERVER_IPADDRESS, DEFAULT_SERVER_IPADDRESS);
236 String portStr = props.getProperty(SERVER_PORT, DEFAULT_SERVER_PORT);
237 String connectionType = props.getProperty(CONNECTION_TYPE, CONNECTION_TYPE_ACTIVE);
239 LOG.info("getPropertiesOptions: Using the following properties: mode= {}, ip:port= {}:{}",
240 connectionType, addressStr, portStr);
242 return new Option[] {
243 editConfigurationFilePut(CUSTOM_PROPERTIES, SERVER_IPADDRESS, addressStr),
244 editConfigurationFilePut(CUSTOM_PROPERTIES, SERVER_PORT, portStr),
245 editConfigurationFilePut(CUSTOM_PROPERTIES, CONNECTION_TYPE, connectionType),
251 public void setup() throws InterruptedException {
253 LOG.info("Skipping setup, already initialized");
259 } catch (Exception e) {
260 LOG.warn("Failed to setup test", e);
261 fail("Failed to setup test: " + e);
263 //dataBroker = getSession().getSALService(DataBroker.class);
265 DataBroker dataBroker = HwvtepSouthboundProvider.getDb();
266 Assert.assertNotNull("db should not be null", dataBroker);
268 addressStr = bundleContext.getProperty(SERVER_IPADDRESS);
269 String portStr = bundleContext.getProperty(SERVER_PORT);
271 portNumber = Integer.parseInt(portStr);
272 } catch (NumberFormatException e) {
273 fail("Invalid port number " + portStr + System.lineSeparator() + usage() + e);
276 connectionType = bundleContext.getProperty(CONNECTION_TYPE);
278 LOG.info("setUp: Using the following properties: mode= {}, ip:port= {}:{}",
279 connectionType, addressStr, portNumber);
280 if (connectionType.equalsIgnoreCase(CONNECTION_TYPE_ACTIVE)) {
281 if (addressStr == null) {
286 mdsalUtils = new MdsalUtils(dataBroker);
287 final ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
288 final InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo);
289 final DataTreeIdentifier<Node> treeId =
290 new DataTreeIdentifier<Node>(LogicalDatastoreType.OPERATIONAL, iid);
292 dataBroker.registerDataTreeChangeListener(treeId, OPERATIONAL_LISTENER);
294 hwvtepNode = connectHwvtepNode(connectionInfo);
295 // Let's count the test methods (we need to use this instead of @AfterClass on teardown() since the latter is
296 // useless with pax-exam)
297 for (Method method : getClass().getMethods()) {
298 boolean testMethod = false;
299 boolean ignoreMethod = false;
300 for (Annotation annotation : method.getAnnotations()) {
301 if (Test.class.equals(annotation.annotationType())) {
304 if (Ignore.class.equals(annotation.annotationType())) {
308 if (testMethod && !ignoreMethod) {
309 testMethodsRemaining++;
312 LOG.info("{} test methods to run", testMethodsRemaining);
317 private Node connectHwvtepNode(ConnectionInfo connectionInfo) throws InterruptedException {
318 final InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo);
319 Assert.assertTrue(mdsalUtils.put(LogicalDatastoreType.CONFIGURATION,
320 iid, HwvtepSouthboundUtils.createNode(connectionInfo)));
321 waitForOperationalCreation(iid);
322 Node node = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid);
323 Assert.assertNotNull(node);
324 LOG.info("Connected to {}", HwvtepSouthboundUtils.connectionInfoToString(connectionInfo));
328 private static void disconnectHwvtepNode(final ConnectionInfo connectionInfo) throws InterruptedException {
329 final InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo);
330 Assert.assertTrue(mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, iid));
331 waitForOperationalDeletion(iid);
332 Node node = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid);
333 Assert.assertNull(node);
334 LOG.info("Disconnected from {}", HwvtepSouthboundUtils.connectionInfoToString(connectionInfo));
337 private void waitForOperationalCreation(InstanceIdentifier<Node> iid) throws InterruptedException {
338 synchronized (OPERATIONAL_LISTENER) {
339 long _start = System.currentTimeMillis();
340 LOG.info("Waiting for OPERATIONAL DataChanged creation on {}", iid);
341 while (!OPERATIONAL_LISTENER.isCreated(
342 iid) && (System.currentTimeMillis() - _start) < OVSDB_ROUNDTRIP_TIMEOUT) {
343 OPERATIONAL_LISTENER.wait(OVSDB_UPDATE_TIMEOUT);
345 LOG.info("Woke up, waited {} for creation of {}", (System.currentTimeMillis() - _start), iid);
349 private static void waitForOperationalDeletion(InstanceIdentifier<Node> iid) throws InterruptedException {
350 synchronized (OPERATIONAL_LISTENER) {
351 long _start = System.currentTimeMillis();
352 LOG.info("Waiting for OPERATIONAL DataChanged deletion on {}", iid);
353 while (!OPERATIONAL_LISTENER.isRemoved(
354 iid) && (System.currentTimeMillis() - _start) < OVSDB_ROUNDTRIP_TIMEOUT) {
355 OPERATIONAL_LISTENER.wait(OVSDB_UPDATE_TIMEOUT);
357 LOG.info("Woke up, waited {} for deletion of {}", (System.currentTimeMillis() - _start), iid);
361 private ConnectionInfo getConnectionInfo(String addressStr, int portNumber) {
362 InetAddress inetAddress = null;
364 inetAddress = InetAddress.getByName(addressStr);
365 } catch (UnknownHostException e) {
366 fail("Could not resolve " + addressStr + ": " + e);
369 IpAddress address = HwvtepSouthboundMapper.createIpAddress(inetAddress);
370 PortNumber port = new PortNumber(portNumber);
372 final ConnectionInfo connectionInfo = new ConnectionInfoBuilder()
373 .setRemoteIp(address)
376 LOG.info("connectionInfo: {}", connectionInfo);
377 return connectionInfo;
380 private static class TestPhysicalSwitch implements AutoCloseable {
381 private final ConnectionInfo connectionInfo;
382 private final String psName;
385 public TestPhysicalSwitch(final ConnectionInfo connectionInfo, String psName) {
386 this(connectionInfo, psName, null, null, null, true, null, null, null);
389 public TestPhysicalSwitch (final ConnectionInfo connectionInfo, final String name,
390 @Nullable InstanceIdentifier<Node> psIid, @Nullable NodeId psNodeId,
391 @Nullable final String description, final boolean setManagedBy,
392 @Nullable final List<ManagementIps> managementIps,
393 @Nullable final List<TunnelIps> tunnelIps,
394 @Nullable final List<Tunnels> tunnels) {
395 this.connectionInfo = connectionInfo;
397 NodeBuilder psNodeBuilder = new NodeBuilder();
399 psIid = HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo, new HwvtepNodeName(psName));
401 if(psNodeId == null) {
402 psNodeId = HwvtepSouthboundMapper.createManagedNodeId(psIid);
404 psNodeBuilder.setNodeId(psNodeId);
405 PhysicalSwitchAugmentationBuilder psAugBuilder = new PhysicalSwitchAugmentationBuilder();
406 psAugBuilder.setHwvtepNodeName(new HwvtepNodeName(psName));
407 if(description != null) {
408 psAugBuilder.setHwvtepNodeDescription(description);
411 InstanceIdentifier<Node> nodePath = HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo);
412 psAugBuilder.setManagedBy(new HwvtepGlobalRef(nodePath));
414 psAugBuilder.setManagementIps(managementIps);
415 psAugBuilder.setTunnelIps(tunnelIps);
416 psAugBuilder.setTunnels(tunnels);
417 psNodeBuilder.addAugmentation(PhysicalSwitchAugmentation.class, psAugBuilder.build());
418 LOG.debug("Built with intent to store PhysicalSwitch data {}", psAugBuilder.toString());
420 mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, psIid, psNodeBuilder.build()));
422 Thread.sleep(OVSDB_UPDATE_TIMEOUT);
423 } catch (InterruptedException e) {
424 LOG.warn("Sleep interrupted while waiting for bridge creation (bridge {})", psName, e);
429 public void close() {
430 final InstanceIdentifier<Node> iid =
431 HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo, new HwvtepNodeName(psName));
432 Assert.assertTrue(mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, iid));
434 Thread.sleep(OVSDB_UPDATE_TIMEOUT);
435 } catch (InterruptedException e) {
436 LOG.warn("Sleep interrupted while waiting for bridge deletion (bridge {})", psName, e);
442 public void teardown() {
443 testMethodsRemaining--;
444 LOG.info("{} test methods remaining", testMethodsRemaining);
448 public void testhwvtepsouthboundFeatureLoad() {
449 Assert.assertTrue(true);
453 public void testNetworkTopology() throws InterruptedException {
454 NetworkTopology networkTopology = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION,
455 InstanceIdentifier.create(NetworkTopology.class));
456 Assert.assertNotNull("NetworkTopology could not be found in " + LogicalDatastoreType.CONFIGURATION,
459 networkTopology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
460 InstanceIdentifier.create(NetworkTopology.class));
461 Assert.assertNotNull("NetworkTopology could not be found in " + LogicalDatastoreType.OPERATIONAL,
466 public void testHwvtepTopology() throws InterruptedException {
467 InstanceIdentifier<Topology> path = InstanceIdentifier
468 .create(NetworkTopology.class)
469 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID));
471 Topology topology = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path);
472 Assert.assertNotNull("Topology could not be found in " + LogicalDatastoreType.CONFIGURATION,
475 topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, path);
477 Assert.assertNotNull("Topology could not be found in " + LogicalDatastoreType.OPERATIONAL,
482 public void testAddDeleteHwvtepNode() throws InterruptedException {
483 ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
484 // At this point we're connected, disconnect and reconnect (the connection will be removed at the very end)
485 disconnectHwvtepNode(connectionInfo);
486 connectHwvtepNode(connectionInfo);
490 public void testAddDeletePhysicalSwitch() throws InterruptedException {
491 ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
493 try (TestPhysicalSwitch testPSwitch = new TestPhysicalSwitch(connectionInfo, PS_NAME)) {
494 PhysicalSwitchAugmentation pSwitch = getPhysicalSwitch(connectionInfo);
495 Assert.assertNotNull(pSwitch);
496 LOG.info("PhysicalSwitch: {}", pSwitch);
500 private PhysicalSwitchAugmentation getPhysicalSwitch(ConnectionInfo connectionInfo) {
501 return getPhysicalSwitch(connectionInfo, PS_NAME);
504 private PhysicalSwitchAugmentation getPhysicalSwitch(ConnectionInfo connectionInfo, String psName) {
505 return getPhysicalSwitch(connectionInfo, psName, LogicalDatastoreType.OPERATIONAL);
508 private PhysicalSwitchAugmentation getPhysicalSwitch(ConnectionInfo connectionInfo, String psName,
509 LogicalDatastoreType dataStore) {
510 Node psNode = getPhysicalSwitchNode(connectionInfo, psName, dataStore);
511 Assert.assertNotNull(psNode);
512 PhysicalSwitchAugmentation psAugmentation = psNode.getAugmentation(PhysicalSwitchAugmentation.class);
513 Assert.assertNotNull(psAugmentation);
514 return psAugmentation;
517 private Node getPhysicalSwitchNode(ConnectionInfo connectionInfo, String psName, LogicalDatastoreType dataStore) {
518 InstanceIdentifier<Node> psIid =
519 HwvtepSouthboundUtils.createInstanceIdentifier(connectionInfo, new HwvtepNodeName(psName));
520 return mdsalUtils.read(dataStore, psIid);