57bc2d7f8db96551bac5e45bb02eea7b177c17b3
[netvirt.git] / vpnservice / it / impl / src / test / java / org / opendaylight / netvirt / it / NetvirtIT.java
1 /*
2  * Copyright (c) 2016 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.netvirt.it;
9
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;
28
29 import com.google.common.collect.Maps;
30 import java.io.File;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
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;
73
74 /**
75  * Integration tests for Netvirt.
76  */
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;
95
96     @Override
97     public MavenUrlReference getFeatureRepo() {
98         return maven()
99                 .groupId("org.opendaylight.netvirt")
100                 .artifactId("it-features")
101                 .classifier("features")
102                 .type("xml")
103                 .versionAsInProject();
104     }
105
106     @Override
107     public String getFeatureName() {
108         return "odl-netvirt-openstack-it";
109     }
110
111     @Configuration
112     @Override
113     public Option[] config() {
114         Option[] tempOptions = combine(super.config(), DockerOvs.getSysPropOptions());
115         return combine(tempOptions, getOtherOptions());
116     }
117
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"),
144                 keepRuntimeFolder()
145         };
146     }
147
148     // This won't get used when we use our own logging.cfg file set in getOtherOptions
149     // but we keep it for reference.
150     @Override
151     public Option getLoggingOption() {
152         return composite(
153                 logLevel(LogLevelOption.LogLevel.INFO),
154                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
155                         logConfiguration(NetvirtIT.class),
156                         INFO.name()),
157                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
158                         "log4j.logger.org.opendaylight.netvirt",
159                         TRACE.name()),
160                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
161                         "log4j.logger.org.opendaylight.genius",
162                         TRACE.name()),
163                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
164                         "log4j.logger.org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils",
165                         TRACE.name()),
166                 /*editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
167                         "log4j.logger.org.opendaylight.openflowplugin.impl",
168                         DEBUG.name()),*/
169                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
170                         "log4j.logger.org.opendaylight.openflowjava.protocol.impl.util.ListDeserializer",
171                         ERROR.name()),
172                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
173                         "log4j.logger.org.opendaylight.controller.configpusherfeature.internal.FeatureConfigPusher",
174                         ERROR.name()),
175                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
176                         "log4j.logger.org.apache.aries.blueprint.container.ServiceRecipe",
177                         WARN.name()),
178                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
179                         "log4j.logger.org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver",
180                         WARN.name()),
181                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
182                         "log4j.logger.org.opendaylight.netvirt.fibmanager.FibNodeCapableListener",
183                         DEBUG.name()),
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:*"));
189     }
190
191     @Before
192     @Override
193     @SuppressWarnings("checkstyle:IllegalCatch")
194     public void setup() throws Exception {
195         if (setup.get()) {
196             LOG.info("Skipping setUp, already initialized");
197             return;
198         }
199
200         try {
201             super.setup();
202         } catch (Exception e) {
203             LOG.warn("Failed to setup test", e);
204             fail("Failed to setup test: " + e);
205         }
206
207         Thread.sleep(10 * 1000);
208         getProperties();
209
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);
219
220         setup.set(true);
221     }
222
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);
233     }
234
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);
249             found = true;
250         }
251         netvirtTopologyListener.close();
252
253         return found;
254     }
255
256     @SuppressWarnings("checkstyle:IllegalCatch")
257     private void validateDefaultFlows(long datapathId, int timeout) {
258         LOG.info("Validating default flows");
259         for (DefaultFlow defaultFlow : DefaultFlow.values()) {
260             try {
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());
266             }
267         }
268     }
269
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));
275     }
276
277     /**
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
285      * </pre>
286      */
287     @Test
288     @SuppressWarnings("checkstyle:IllegalCatch")
289     public void testNetVirt() throws InterruptedException {
290         int ovs1 = 1;
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);
296
297             NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
298
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());
303         }
304     }
305
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.";
309
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.";
313
314     private static final String ROUTER1_NAME = "router1";
315
316     /**
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
320      */
321     @Test
322     @SuppressWarnings("checkstyle:IllegalCatch")
323     public void testNeutronNet() throws InterruptedException {
324         int ovs1 = 1;
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);
331
332             //Creating default SG
333             LOG.info("Installing default SG");
334             List<Uuid> sgList = new ArrayList<>();
335             sgList.add(neutronSecurityGroupUtils.createDefaultSG());
336
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);
340
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");
344             if (!isUserSpace) {
345                 LOG.info("Ping status rc: {}", rc);
346             }
347
348             destroyOvs(netOvs);
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());
353         }
354     }
355
356     @Test
357     @SuppressWarnings("checkstyle:IllegalCatch")
358     public void testNeutronNetL3() throws InterruptedException {
359         int ovs1 = 1;
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);
365
366             //create 2 networks
367             netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
368             netOvs.createNetwork(NETWORK2_NAME, NETWORK2_SEGID, NETWORK2_IPPFX);
369
370             //Creating default SG
371             LOG.info("Installing default SG");
372             List<Uuid> sgList = new ArrayList<>();
373             sgList.add(neutronSecurityGroupUtils.createDefaultSG());
374
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);
379
380             int rc = netOvs.ping(port1, port2);
381             netOvs.logState(ovs1, "after ping without router");
382             assertTrue("Ping should fail without router", rc != 0);
383
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);
388
389             rc = netOvs.ping(port1, port2);
390             netOvs.logState(ovs1, "after ping with router");
391             assertTrue("Ping with router", rc == 0);
392
393             destroyOvs(netOvs);
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());
398         }
399     }
400
401     // This test requires ovs kernel modules to be loaded which is not in jenkins yet.
402     @Test
403     @SuppressWarnings("checkstyle:IllegalCatch")
404     public void testNeutronNetTwoNodes() throws InterruptedException {
405         int ovs1 = 1;
406         int ovs2 = 2;
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);
412
413             netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
414
415             //Creating default SG
416             LOG.info("Installing default SG");
417             List<Uuid> sgList = new ArrayList<>();
418             sgList.add(neutronSecurityGroupUtils.createDefaultSG());
419
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);
424
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");
429             if (!isUserSpace) {
430                 LOG.info("Ping status rc: {}", rc);
431             }
432
433             destroyOvs(netOvs);
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());
439         }
440     }
441
442     @Test
443     @SuppressWarnings("checkstyle:IllegalCatch")
444     public void testProviderNetTwoNodes() throws InterruptedException {
445         int ovs1 = 1;
446         int ovs2 = 2;
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");
450
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");
459                 return;
460             }
461             Boolean isUserSpace = userSpaceEnabled.equals("yes");
462             LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
463
464             NetOvs netOvs = getNetOvs(ovs, isUserSpace);
465
466             NodeInfo nodeInfo = connectOvs(netOvs, ovs1, ovs);
467             NodeInfo nodeInfo2 = connectOvs(netOvs, ovs2, ovs);
468
469             netOvs.createFlatNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX, PHYSNET);
470
471             String port1 = addPort(netOvs, nodeInfo, ovs1, NETWORK1_NAME, null);
472             String port2 = addPort(netOvs, nodeInfo2, ovs2, NETWORK1_NAME, null);
473
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");
478             if (!isUserSpace) {
479                 LOG.info("Ping status rc: {}", rc);
480             }
481
482             destroyOvs(netOvs);
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());
488         } finally {
489             if (controllerIpAddress != null) {
490                 props.setProperty(ItConstants.CONTROLLER_IPADDRESS, controllerIpAddress);
491             }
492         }
493     }
494
495     // This test requires ovs kernel modules to be loaded which is not in jenkins yet.
496     @Test
497     @SuppressWarnings("checkstyle:IllegalCatch")
498     public void testNeutronNetL3TwoNodes() throws InterruptedException {
499         int ovs1 = 1;
500         int ovs2 = 2;
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);
506
507             //Creating default SG
508             LOG.info("Installing default SG");
509             List<Uuid> sgList = new ArrayList<>();
510             sgList.add(neutronSecurityGroupUtils.createDefaultSG());
511
512             //create 2 networks
513             netOvs.createNetwork(NETWORK1_NAME, NETWORK1_SEGID, NETWORK1_IPPFX);
514             netOvs.createNetwork(NETWORK2_NAME, NETWORK2_SEGID, NETWORK2_IPPFX);
515
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);
521
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);
526
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);
531
532             waitForTunnels();
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");
536             if (!isUserSpace) {
537                 assertTrue("Ping with router", rc == 0);
538             }
539
540             destroyOvs(netOvs);
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());
546         }
547     }
548
549     private NetOvs getNetOvs(DockerOvs ovs, Boolean isUserSpace) {
550         NetOvs netOvs;
551         if (ovs.usingExternalDocker()) {
552             netOvs = new RealNetOvsImpl(ovs, isUserSpace, mdsalUtils, southboundUtils);
553         } else {
554             netOvs = new DockerNetOvsImpl(ovs, isUserSpace, mdsalUtils, southboundUtils);
555         }
556         return netOvs;
557     }
558
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);
565         nodeInfo.connect();
566         LOG.info("connectOvs: node {} should be connected: {}",
567                 ovsInstance, nodeInfo.ovsdbNode.getNodeId());
568         String localIp = netOvs.getInstanceIp(ovsInstance);
569         addLocalIp(nodeInfo, localIp);
570
571         validateDefaultFlows(nodeInfo.datapathId, 2 * 60 * 1000);
572         netOvs.logState(ovsInstance, "node " + ovsInstance + " default flows");
573         LOG.info("connectOvs exit: netOvs {}", ovsInstance);
574         return nodeInfo;
575     }
576
577     private void disconnectOvs(NodeInfo nodeInfo) throws Exception {
578         LOG.info("disconnectOvs enter: {}", nodeInfo.ovsdbNode.getNodeId().getValue());
579         nodeInfo.disconnect();
580         Thread.sleep(5000);
581         LOG.info("disconnectOvs exit: {}", nodeInfo.ovsdbNode.getNodeId().getValue());
582     }
583
584     private void destroyOvs(NetOvs netOvs) throws InterruptedException {
585         LOG.info("destroyOvs enter");
586         netOvs.destroy();
587         // This sleep allows netvirt and genius to run properly cleanup of neutron ports
588         // and networks deleted by destroy()
589         Thread.sleep(5000);
590         LOG.info("destroyOvs exit");
591     }
592
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);
598
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);
605
606         netOvs.preparePortForPing(port);
607
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
613         Thread.sleep(30000);
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);
618         return port;
619     }
620
621     private void addRouter(NetOvs netOvs, String routerName) throws Exception {
622         LOG.info("addRouter enter: {}", routerName);
623         String routerId = netOvs.createRouter(routerName);
624
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)))
628                 .build();
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);
636     }
637
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");
649                     break;
650                 } else {
651                     LOG.info("waitForTunnels try {}, size: {}", i, tunnelsState.getStateTunnelList().size());
652                 }
653             } else {
654                 Thread.sleep(1000);
655             }
656         }
657         Thread.sleep(3000);
658         LOG.info("waitForTunnels exit");
659     }
660 }