2 * Copyright (c) 2016 Red Hat, Inc. 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.netvirt.it;
10 import static org.junit.Assert.assertNotNull;
11 import static org.junit.Assert.assertTrue;
12 import static org.junit.Assert.fail;
13 import static org.ops4j.pax.exam.CoreOptions.composite;
14 import static org.ops4j.pax.exam.CoreOptions.maven;
15 import static org.ops4j.pax.exam.CoreOptions.vmOption;
16 import static org.ops4j.pax.exam.CoreOptions.when;
17 import static org.ops4j.pax.exam.OptionUtils.combine;
18 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
19 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
20 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
21 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
22 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.replaceConfigurationFile;
23 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.DEBUG;
24 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.ERROR;
25 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.INFO;
26 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.TRACE;
27 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.WARN;
29 import com.google.common.collect.Maps;
31 import java.util.ArrayList;
32 import java.util.List;
34 import java.util.Properties;
35 import java.util.concurrent.atomic.AtomicBoolean;
36 import javax.inject.Inject;
37 import org.junit.Before;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
41 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
42 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
43 import org.opendaylight.netvirt.it.NetvirtITConstants.DefaultFlow;
44 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
45 import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
46 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.DockerOvs;
47 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.ItConstants;
48 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.NodeInfo;
49 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.OvsdbItUtils;
50 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.ops4j.pax.exam.Configuration;
64 import org.ops4j.pax.exam.Option;
65 import org.ops4j.pax.exam.junit.PaxExam;
66 import org.ops4j.pax.exam.karaf.options.LogLevelOption;
67 import org.ops4j.pax.exam.options.MavenUrlReference;
68 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
69 import org.ops4j.pax.exam.spi.reactors.PerClass;
70 import org.ops4j.pax.exam.util.Filter;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
75 * Integration tests for Netvirt.
77 @RunWith(PaxExam.class)
78 @ExamReactorStrategy(PerClass.class)
79 public class NetvirtIT extends AbstractMdsalTestBase {
80 private static final Logger LOG = LoggerFactory.getLogger(NetvirtIT.class);
81 private static final String PHYSNET = "physnet";
82 private static OvsdbItUtils itUtils;
83 private static MdsalUtils mdsalUtils = null;
84 private static SouthboundUtils southboundUtils;
85 private static org.opendaylight.netvirt.it.SouthboundUtils nvSouthboundUtils;
86 private static FlowITUtil flowITUtil;
87 private static AtomicBoolean setup = new AtomicBoolean(false);
88 private static final String NETVIRT_TOPOLOGY_ID = "netvirt:1";
89 @Inject @Filter(timeout = 60000)
90 private static DataBroker dataBroker = null;
91 private static String userSpaceEnabled;
92 private static final String OVS_ONE_NODE_YML = "ovs-2.5.0-hwvtep.yml";
93 private static final String OVS_TWO_NODE_YML = "two_" + OVS_ONE_NODE_YML;
94 private static NeutronSecurityGroupUtils neutronSecurityGroupUtils;
97 public MavenUrlReference getFeatureRepo() {
99 .groupId("org.opendaylight.netvirt")
100 .artifactId("it-features")
101 .classifier("features")
103 .versionAsInProject();
107 public String getFeatureName() {
108 return "odl-netvirt-openstack-it";
113 public Option[] config() {
114 Option[] tempOptions = combine(super.config(), DockerOvs.getSysPropOptions());
115 return combine(tempOptions, getOtherOptions());
118 private Option[] getOtherOptions() {
119 return new Option[] {
120 configureConsole().startLocalConsole(),
121 // Use transparent as the default
122 when("transparent".equals(System.getProperty("sgm", "transparent"))).useOptions(
123 replaceConfigurationFile(
124 "etc/opendaylight/datastore/initial/config/netvirt-aclservice-config.xml",
125 new File("src/test/resources/initial/netvirt-aclservice-config-transparent.xml"))),
126 when("learn".equals(System.getProperty("sgm"))).useOptions(
127 replaceConfigurationFile(
128 "etc/opendaylight/datastore/initial/config/netvirt-aclservice-config.xml",
129 new File("src/test/resources/initial/netvirt-aclservice-config-learn.xml"))),
130 when("stateful".equals(System.getProperty("sgm"))).useOptions(
131 replaceConfigurationFile(
132 "etc/opendaylight/datastore/initial/config/netvirt-aclservice-config.xml",
133 new File("src/test/resources/initial/netvirt-aclservice-config-stateful.xml"))),
134 when("stateless".equals(System.getProperty("sgm"))).useOptions(
135 replaceConfigurationFile(
136 "etc/opendaylight/datastore/initial/config/netvirt-aclservice-config.xml",
137 new File("src/test/resources/initial/netvirt-aclservice-config-stateless.xml"))),
138 // Add our own logging.cfg so we can log to a single karaf.log file
139 replaceConfigurationFile("etc/org.ops4j.pax.logging.cfg",
140 new File("src/test/resources/org.ops4j.pax.logging.cfg")),
141 vmOption("-javaagent:../jars/org.jacoco.agent.jar=destfile=../../jacoco-it.exec"),
142 vmOption("-Xmx2048m"),
143 //vmOption("-XX:MaxPermSize=m"),
148 // This won't get used when we use our own logging.cfg file set in getOtherOptions
149 // but we keep it for reference.
151 public Option getLoggingOption() {
153 logLevel(LogLevelOption.LogLevel.INFO),
154 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
155 logConfiguration(NetvirtIT.class),
157 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
158 "log4j.logger.org.opendaylight.netvirt",
160 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
161 "log4j.logger.org.opendaylight.genius",
163 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
164 "log4j.logger.org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils",
166 /*editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
167 "log4j.logger.org.opendaylight.openflowplugin.impl",
169 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
170 "log4j.logger.org.opendaylight.openflowjava.protocol.impl.util.ListDeserializer",
172 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
173 "log4j.logger.org.opendaylight.controller.configpusherfeature.internal.FeatureConfigPusher",
175 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
176 "log4j.logger.org.apache.aries.blueprint.container.ServiceRecipe",
178 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
179 "log4j.logger.org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver",
181 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
182 "log4j.logger.org.opendaylight.netvirt.fibmanager.FibNodeCapableListener",
184 super.getLoggingOption());
185 // TODO trying to get console logged to karaf.log, but doesn't work.
186 // wondering if the test stops and the log isn't flushed?
187 //editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
188 // "log4j.rootLogger", "INFO, async, stdout, osgi:*"));
193 @SuppressWarnings("checkstyle:IllegalCatch")
194 public void setup() throws Exception {
196 LOG.info("Skipping setUp, already initialized");
202 } catch (Exception e) {
203 LOG.warn("Failed to setup test", e);
204 fail("Failed to setup test: " + e);
207 Thread.sleep(10 * 1000);
210 assertNotNull("dataBroker should not be null", dataBroker);
211 itUtils = new OvsdbItUtils(dataBroker);
212 mdsalUtils = new MdsalUtils(dataBroker);
213 assertNotNull("mdsalUtils should not be null", mdsalUtils);
214 southboundUtils = new SouthboundUtils(mdsalUtils);
215 nvSouthboundUtils = new org.opendaylight.netvirt.it.SouthboundUtils(mdsalUtils);
216 assertTrue("Did not find " + NETVIRT_TOPOLOGY_ID, getNetvirtTopology());
217 flowITUtil = new FlowITUtil(dataBroker);
218 neutronSecurityGroupUtils = new NeutronSecurityGroupUtils(mdsalUtils);
223 private void getProperties() {
224 Properties props = System.getProperties();
225 String addressStr = props.getProperty(NetvirtITConstants.SERVER_IPADDRESS);
226 String portStr = props.getProperty(NetvirtITConstants.SERVER_PORT, NetvirtITConstants.DEFAULT_SERVER_PORT);
227 String connectionType = props.getProperty(NetvirtITConstants.CONNECTION_TYPE, "active");
228 String controllerStr = props.getProperty(NetvirtITConstants.CONTROLLER_IPADDRESS, "0.0.0.0");
229 userSpaceEnabled = props.getProperty(NetvirtITConstants.USERSPACE_ENABLED, "no");
230 LOG.info("setUp: Using the following properties: mode= {}, ip:port= {}:{}, controller ip: {}, "
231 + "userspace.enabled: {}",
232 connectionType, addressStr, portStr, controllerStr, userSpaceEnabled);
235 private Boolean getNetvirtTopology() throws Exception {
236 LOG.info("getNetvirtTopology: looking for {}...", NETVIRT_TOPOLOGY_ID);
237 Boolean found = false;
238 TopologyId topologyId = new TopologyId(NETVIRT_TOPOLOGY_ID);
239 InstanceIdentifier<Topology> path =
240 InstanceIdentifier.create(NetworkTopology.class).child(Topology.class, new TopologyKey(topologyId));
241 final NotifyingDataChangeListener netvirtTopologyListener =
242 new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL,
243 NotifyingDataChangeListener.BIT_CREATE, path, null);
244 netvirtTopologyListener.registerDataChangeListener(dataBroker);
245 netvirtTopologyListener.waitForCreation(60000);
246 Topology topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, path);
247 if (topology != null) {
248 LOG.info("getNetvirtTopology: found {}...", NETVIRT_TOPOLOGY_ID);
251 netvirtTopologyListener.close();
256 @SuppressWarnings("checkstyle:IllegalCatch")
257 private void validateDefaultFlows(long datapathId, int timeout) {
258 LOG.info("Validating default flows");
259 for (DefaultFlow defaultFlow : DefaultFlow.values()) {
261 flowITUtil.verifyFlowByFields(datapathId, defaultFlow.getFlowId(), defaultFlow.getTableId(), timeout);
262 //flowITUtil.verifyFlowById(datapathId, defaultFlow.getFlowId(), defaultFlow.getTableId());
263 } catch (Exception e) {
264 LOG.error("Failed to verify flow id : {}", defaultFlow.getFlowId());
265 fail("Failed to verify flow id : " + defaultFlow.getFlowId());
270 private void addLocalIp(NodeInfo nodeInfo, String ip) {
271 LOG.info("addlocalIp: nodeinfo: {}, local_ip: {}", nodeInfo.ovsdbNode.getNodeId(), ip);
272 Map<String, String> otherConfigs = Maps.newHashMap();
273 otherConfigs.put("local_ip", ip);
274 assertTrue(nvSouthboundUtils.addOpenVSwitchOtherConfig(nodeInfo.ovsdbNode, otherConfigs));
278 * Test for basic southbound events to netvirt.
279 * <pre>The test will:
280 * - connect to an OVSDB node and verify it is added to operational
281 * - then verify that br-int was created on the node and stored in operational
282 * - a port is then added to the bridge to verify that it is ignored by netvirt
283 * - remove the bridge
284 * - remove the node and verify it is not in operational
288 @SuppressWarnings("checkstyle:IllegalCatch")
289 public void testNetVirt() throws InterruptedException {
291 System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, OVS_ONE_NODE_YML);
292 try (DockerOvs ovs = new DockerOvs()) {
293 Boolean isUserSpace = userSpaceEnabled.equals("yes");
294 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
295 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
297 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
299 disconnectOvs(nodeInfo);
300 } catch (Exception e) {
301 LOG.error("testNetVirt: Exception thrown by OvsDocker.OvsDocker()", e);
302 fail("testNetVirt: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
306 private static final String NETWORK1_NAME = "net1";
307 private static final String NETWORK1_SEGID = "101";
308 private static final String NETWORK1_IPPFX = "10.1.1.";
310 private static final String NETWORK2_NAME = "net2";
311 private static final String NETWORK2_SEGID = "201";
312 private static final String NETWORK2_IPPFX = "20.1.1.";
314 private static final String ROUTER1_NAME = "router1";
317 * Test a basic neutron use case. This test constructs a Neutron network, subnet, and two "vm" ports
318 * and validates that the correct flows are installed on OVS. Then it pings from one VM port to the other.
319 * @throws InterruptedException if we're interrupted while waiting for some mdsal operation to complete
322 @SuppressWarnings("checkstyle:IllegalCatch")
323 public void testNeutronNet() throws InterruptedException {
325 System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, OVS_ONE_NODE_YML);
326 try (DockerOvs ovs = new DockerOvs()) {
327 Boolean isUserSpace = userSpaceEnabled.equals("yes");
328 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
329 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
330 netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
332 //Creating default SG
333 LOG.info("Installing default SG");
334 List<Uuid> sgList = new ArrayList<>();
335 sgList.add(neutronSecurityGroupUtils.createDefaultSG());
337 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
338 String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, sgList);
339 String port2 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, sgList);
341 int rc = netOvs.ping(port1, port2);
342 LOG.info("Ping status rc: {}, ignored for isUserSpace: {}", rc, isUserSpace);
343 netOvs.logState(ovs1, "node 1 after ping");
345 LOG.info("Ping status rc: {}", rc);
349 disconnectOvs(nodeInfo);
350 } catch (Exception e) {
351 LOG.error("testNeutronNet: Exception thrown by OvsDocker.OvsDocker()", e);
352 fail("testNeutronNet: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
357 @SuppressWarnings("checkstyle:IllegalCatch")
358 public void testNeutronNetL3() throws InterruptedException {
360 System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, OVS_ONE_NODE_YML);
361 try (DockerOvs ovs = new DockerOvs()) {
362 Boolean isUserSpace = userSpaceEnabled.equals("yes");
363 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
364 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
367 netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
368 netOvs.createNetwork(NETWORK2_NAME, NETWORK2_SEGID, NETWORK2_IPPFX);
370 //Creating default SG
371 LOG.info("Installing default SG");
372 List<Uuid> sgList = new ArrayList<>();
373 sgList.add(neutronSecurityGroupUtils.createDefaultSG());
375 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
376 //create 2 "vms" ports
377 String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, sgList);
378 String port2 = addPort(netOvs, nodeInfo, ovs1, NETWORK2_NAME, sgList);
380 int rc = netOvs.ping(port1, port2);
381 netOvs.logState(ovs1, "after ping without router");
382 assertTrue("Ping should fail without router", rc != 0);
384 //create neutron router and add the networks
385 addRouter(netOvs, ROUTER1_NAME);
386 netOvs.createRouterInterface(ROUTER1_NAME, NETWORK1_NAME);
387 netOvs.createRouterInterface(ROUTER1_NAME, NETWORK2_NAME);
389 rc = netOvs.ping(port1, port2);
390 netOvs.logState(ovs1, "after ping with router");
391 assertTrue("Ping with router", rc == 0);
394 disconnectOvs(nodeInfo);
395 } catch (Exception e) {
396 LOG.error("testNeutronNetL3: Exception thrown by OvsDocker.OvsDocker()", e);
397 fail("testNeutronNetL3: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
401 // This test requires ovs kernel modules to be loaded which is not in jenkins yet.
403 @SuppressWarnings("checkstyle:IllegalCatch")
404 public void testNeutronNetTwoNodes() throws InterruptedException {
407 System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, OVS_TWO_NODE_YML);
408 try (DockerOvs ovs = new DockerOvs()) {
409 Boolean isUserSpace = userSpaceEnabled.equals("yes");
410 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
411 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
413 netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
415 //Creating default SG
416 LOG.info("Installing default SG");
417 List<Uuid> sgList = new ArrayList<>();
418 sgList.add(neutronSecurityGroupUtils.createDefaultSG());
420 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
421 NodeInfo nodeInfo2 = connectOvs(netOvs, ovs2, ovs);
422 String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, sgList);
423 String port2 = addPort(netOvs, nodeInfo2, ovs2, NETWORK1_NAME, sgList);
425 int rc = netOvs.ping(port1, port2);
426 LOG.info("Ping status rc: {}, ignored for isUserSpace: {}", rc, isUserSpace);
427 netOvs.logState(ovs1, "node 1 after ping");
428 netOvs.logState(ovs2, "node 2 after ping");
430 LOG.info("Ping status rc: {}", rc);
434 disconnectOvs(nodeInfo);
435 disconnectOvs(nodeInfo2);
436 } catch (Exception e) {
437 LOG.error("testNeutronNetTwoNodes: Exception thrown by OvsDocker.OvsDocker()", e);
438 fail("testNeutronNetTwoNodes: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
443 @SuppressWarnings("checkstyle:IllegalCatch")
444 public void testProviderNetTwoNodes() throws InterruptedException {
447 Properties props = System.getProperties();
448 props.setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, "two_ovs-2.5.1-dual-nic.yml");
449 props.setProperty(ItConstants.DOCKER_WAIT_FOR_PING_SECS, "20");
451 //Remove the ovsdb.controller.ipaddress to force DockerOvs to create it's own network
452 //since that is the only way this docker compose file works (it uses the "odl" network)
453 //We reset the env. in the finally clause
454 String controllerIpAddress = props.getProperty(ItConstants.CONTROLLER_IPADDRESS);
455 props.remove(ItConstants.CONTROLLER_IPADDRESS);
456 try (DockerOvs ovs = new DockerOvs()) {
457 if (ovs.usingExternalDocker()) {
458 LOG.debug("testProviderNetTwoNodes - Not configured to run docker, skipping this test");
461 Boolean isUserSpace = userSpaceEnabled.equals("yes");
462 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
464 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
466 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
467 NodeInfo nodeInfo2 = connectOvs(netOvs, ovs2, ovs);
469 netOvs.createFlatNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX, PHYSNET);
471 String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, null);
472 String port2 = addPort(netOvs, nodeInfo2, ovs2, NETWORK1_NAME, null);
474 int rc = netOvs.ping(port1, port2);
475 LOG.info("Ping status rc: {}, ignored for isUserSpace: {}", rc, isUserSpace);
476 netOvs.logState(ovs1, "node 1 after ping");
477 netOvs.logState(ovs2, "node 2 after ping");
479 LOG.info("Ping status rc: {}", rc);
483 disconnectOvs(nodeInfo);
484 disconnectOvs(nodeInfo2);
485 } catch (Exception e) {
486 LOG.error("testProviderNet: Exception thrown by OvsDocker.OvsDocker()", e);
487 fail("testProviderNet: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
489 if (controllerIpAddress != null) {
490 props.setProperty(ItConstants.CONTROLLER_IPADDRESS, controllerIpAddress);
495 // This test requires ovs kernel modules to be loaded which is not in jenkins yet.
497 @SuppressWarnings("checkstyle:IllegalCatch")
498 public void testNeutronNetL3TwoNodes() throws InterruptedException {
501 System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, OVS_TWO_NODE_YML);
502 try (DockerOvs ovs = new DockerOvs()) {
503 Boolean isUserSpace = userSpaceEnabled.equals("yes");
504 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
505 NetOvs netOvs = getNetOvs(ovs, isUserSpace);
507 //Creating default SG
508 LOG.info("Installing default SG");
509 List<Uuid> sgList = new ArrayList<>();
510 sgList.add(neutronSecurityGroupUtils.createDefaultSG());
513 netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
514 netOvs.createNetwork(NETWORK2_NAME, NETWORK2_SEGID, NETWORK2_IPPFX);
516 NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
517 NodeInfo nodeInfo2 = connectOvs(netOvs, ovs2, ovs);
518 //create 2 "vms" ports
519 String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, sgList);
520 String port2 = addPort(netOvs, nodeInfo2, ovs2, NETWORK2_NAME, sgList);
522 int rc = netOvs.ping(port1, port2);
523 netOvs.logState(ovs1, "node 1 after ping without router");
524 netOvs.logState(ovs2, "node 2 after ping without router");
525 assertTrue("Ping should fail without router", rc != 0);
527 //create neutron router and add the networks
528 addRouter(netOvs, ROUTER1_NAME);
529 netOvs.createRouterInterface(ROUTER1_NAME, NETWORK1_NAME);
530 netOvs.createRouterInterface(ROUTER1_NAME, NETWORK2_NAME);
533 rc = netOvs.ping(port1, port2);
534 netOvs.logState(ovs1, "node 1 after ping with router");
535 netOvs.logState(ovs2, "node 2 after ping with router");
537 assertTrue("Ping with router", rc == 0);
541 disconnectOvs(nodeInfo);
542 disconnectOvs(nodeInfo2);
543 } catch (Exception e) {
544 LOG.error("testNeutronNetL3TwoNodes: Exception thrown by OvsDocker.OvsDocker()", e);
545 fail("testNeutronNetL3TwoNodes: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
549 private NetOvs getNetOvs(DockerOvs ovs, Boolean isUserSpace) {
551 if (ovs.usingExternalDocker()) {
552 netOvs = new RealNetOvsImpl(ovs, isUserSpace, mdsalUtils, southboundUtils);
554 netOvs = new DockerNetOvsImpl(ovs, isUserSpace, mdsalUtils, southboundUtils);
559 private NodeInfo connectOvs(NetOvs netOvs, int ovsInstance, DockerOvs ovs) throws Exception {
560 LOG.info("connectOvs enter: netOvs {}", ovsInstance);
561 netOvs.logState(ovsInstance, "node " + ovsInstance + " idle");
562 ConnectionInfo connectionInfo =
563 SouthboundUtils.getConnectionInfo(ovs.getOvsdbAddress(ovsInstance), ovs.getOvsdbPort(ovsInstance));
564 NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
566 LOG.info("connectOvs: node {} should be connected: {}",
567 ovsInstance, nodeInfo.ovsdbNode.getNodeId());
568 String localIp = netOvs.getInstanceIp(ovsInstance);
569 addLocalIp(nodeInfo, localIp);
571 validateDefaultFlows(nodeInfo.datapathId, 2 * 60 * 1000);
572 netOvs.logState(ovsInstance, "node " + ovsInstance + " default flows");
573 LOG.info("connectOvs exit: netOvs {}", ovsInstance);
577 private void disconnectOvs(NodeInfo nodeInfo) throws Exception {
578 LOG.info("disconnectOvs enter: {}", nodeInfo.ovsdbNode.getNodeId().getValue());
579 nodeInfo.disconnect();
581 LOG.info("disconnectOvs exit: {}", nodeInfo.ovsdbNode.getNodeId().getValue());
584 private void destroyOvs(NetOvs netOvs) throws InterruptedException {
585 LOG.info("destroyOvs enter");
587 // This sleep allows netvirt and genius to run properly cleanup of neutron ports
588 // and networks deleted by destroy()
590 LOG.info("destroyOvs exit");
593 private String addPort(NetOvs netOvs, NodeInfo nodeInfo, int ovsInstance, String networkName,
594 List<Uuid> securityGroupList) throws Exception {
595 String port = netOvs.createPort(ovsInstance, nodeInfo.bridgeNode, networkName, securityGroupList);
596 LOG.info("addPort enter: Bridge node: {}, Created port: {} on network: {}",
597 nodeInfo.bridgeNode.getNodeId().getValue(), netOvs.getPortInfo(port), networkName);
599 InstanceIdentifier<TerminationPoint> tpIid =
600 southboundUtils.createTerminationPointInstanceIdentifier(nodeInfo.bridgeNode, port);
601 final NotifyingDataChangeListener portOperationalListener =
602 new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL,
603 NotifyingDataChangeListener.BIT_CREATE, tpIid, null);
604 portOperationalListener.registerDataChangeListener(dataBroker);
606 netOvs.preparePortForPing(port);
608 portOperationalListener.waitForCreation(10000);
609 portOperationalListener.clear();
610 portOperationalListener.close();
611 // TODO: find better wait condition, what event indicates the port is added
612 // in the models and is ready for use
614 netOvs.logState(ovsInstance, "node " + ovsInstance + " " + nodeInfo.bridgeNode.getNodeId().getValue()
615 + " after port " + netOvs.getPortInfo(port));
616 LOG.info("addPort exit: Bridge node: {}, Created port: {} on network: {}",
617 nodeInfo.bridgeNode.getNodeId().getValue(), netOvs.getPortInfo(port), networkName);
621 private void addRouter(NetOvs netOvs, String routerName) throws Exception {
622 LOG.info("addRouter enter: {}", routerName);
623 String routerId = netOvs.createRouter(routerName);
625 //wait for VpnMap update before starting to use the router
626 InstanceIdentifier<VpnMap> vpnIid = InstanceIdentifier.builder(VpnMaps.class)
627 .child(VpnMap.class, new VpnMapKey(new Uuid(routerId)))
629 final NotifyingDataChangeListener vpnMapListener =
630 new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION,
631 NotifyingDataChangeListener.BIT_CREATE, vpnIid, null);
632 vpnMapListener.registerDataChangeListener(dataBroker);
633 vpnMapListener.waitForCreation(10000);
634 vpnMapListener.close();
635 LOG.info("addRouter exit: {}", routerName);
638 private void waitForTunnels() throws InterruptedException {
639 LOG.info("waitForTunnels enter");
640 InstanceIdentifier<TunnelsState> tunIid = InstanceIdentifier.builder(TunnelsState.class).build();
641 for (int i = 0; i < 10; i++) {
642 TunnelsState tunnelsState = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, tunIid);
643 LOG.info("waitForTunnels try {}, {}", i, tunnelsState);
644 if (tunnelsState != null && tunnelsState.getStateTunnelList() != null) {
645 // TODO: add more verification to validate the two tunnels are the right ones
646 // i.e. check host ips or other parts of the model
647 if (tunnelsState.getStateTunnelList().size() == 2) {
648 LOG.info("waitForTunnels found both tunnels");
651 LOG.info("waitForTunnels try {}, size: {}", i, tunnelsState.getStateTunnelList().size());
658 LOG.info("waitForTunnels exit");