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.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14 import static org.ops4j.pax.exam.CoreOptions.composite;
15 import static org.ops4j.pax.exam.CoreOptions.maven;
16 import static org.ops4j.pax.exam.CoreOptions.vmOption;
17 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
18 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
19 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
20 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.replaceConfigurationFile;
21 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.DEBUG;
22 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.ERROR;
23 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.INFO;
24 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.TRACE;
25 import static org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel.WARN;
27 import com.google.common.collect.Maps;
30 import java.util.Properties;
31 import java.util.concurrent.atomic.AtomicBoolean;
32 import javax.inject.Inject;
33 import org.junit.Before;
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.common.api.data.LogicalDatastoreType;
38 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
39 import org.opendaylight.netvirt.it.NetvirtITConstants.DefaultFlow;
40 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
41 import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
42 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.DockerOvs;
43 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.NodeInfo;
44 import org.opendaylight.ovsdb.utils.ovsdb.it.utils.OvsdbItUtils;
45 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.ops4j.pax.exam.Configuration;
54 import org.ops4j.pax.exam.Option;
55 import org.ops4j.pax.exam.junit.PaxExam;
56 import org.ops4j.pax.exam.options.MavenUrlReference;
57 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
58 import org.ops4j.pax.exam.spi.reactors.PerClass;
59 import org.ops4j.pax.exam.util.Filter;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * Integration tests for Netvirt.
66 @RunWith(PaxExam.class)
67 @ExamReactorStrategy(PerClass.class)
68 public class NetvirtIT extends AbstractMdsalTestBase {
69 private static final Logger LOG = LoggerFactory.getLogger(NetvirtIT.class);
70 private static OvsdbItUtils itUtils;
71 private static MdsalUtils mdsalUtils = null;
72 private static SouthboundUtils southboundUtils;
73 private static org.opendaylight.netvirt.it.SouthboundUtils nvSouthboundUtils;
74 private static FlowITUtil flowITUtil;
75 private static AtomicBoolean setup = new AtomicBoolean(false);
76 private static final String NETVIRT_TOPOLOGY_ID = "netvirt:1";
77 @Inject @Filter(timeout = 60000)
78 private static DataBroker dataBroker = null;
79 private static String userSpaceEnabled;
82 public MavenUrlReference getFeatureRepo() {
84 .groupId("org.opendaylight.netvirt")
85 .artifactId("it-features")
86 .classifier("features")
88 .versionAsInProject();
92 public String getFeatureName() {
93 return "odl-netvirt-openstack-it";
98 public Option[] config() {
99 Option[] ovsProps = super.config();
100 Option[] propertiesOptions = DockerOvs.getSysPropOptions();
101 Option[] otherOptions = getOtherOptions();
102 Option[] options = new Option[ovsProps.length + propertiesOptions.length + otherOptions.length];
103 System.arraycopy(ovsProps, 0, options, 0, ovsProps.length);
104 System.arraycopy(propertiesOptions, 0, options, ovsProps.length, propertiesOptions.length);
105 System.arraycopy(otherOptions, 0, options, ovsProps.length + propertiesOptions.length,
106 otherOptions.length);
110 private Option[] getOtherOptions() {
111 return new Option[] {
112 configureConsole().startLocalConsole(),
113 //when("transparent".equals(System.getProperty("sgm"))).useOptions(
114 replaceConfigurationFile(
115 "etc/opendaylight/datastore/initial/config/netvirt-aclservice-config.xml",
116 new File("src/test/resources/initial/netvirt-aclservice-config.xml")),//),
117 vmOption("-javaagent:../jars/org.jacoco.agent.jar=destfile=../../jacoco-it.exec"),
123 public Option getLoggingOption() {
125 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
126 logConfiguration(NetvirtIT.class),
128 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
129 "log4j.logger.org.opendaylight.netvirt",
131 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
132 "log4j.logger.org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils",
134 /*editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
135 "log4j.logger.org.opendaylight.openflowplugin.impl",
137 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
138 "log4j.logger.org.opendaylight.openflowjava.protocol.impl.util.ListDeserializer",
140 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
141 "log4j.logger.org.opendaylight.controller.configpusherfeature.internal.FeatureConfigPusher",
143 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
144 "log4j.logger.org.apache.aries.blueprint.container.ServiceRecipe",
146 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
147 "log4j.logger.org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver",
149 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
150 "log4j.logger.org.opendaylight.netvirt.fibmanager.FibNodeCapableListener",
152 super.getLoggingOption());
157 @SuppressWarnings("checkstyle:IllegalCatch")
158 public void setup() throws Exception {
160 LOG.info("Skipping setUp, already initialized");
166 } catch (Exception e) {
167 LOG.warn("Failed to setup test", e);
168 fail("Failed to setup test: " + e);
171 Thread.sleep(10 * 1000);
174 assertNotNull("dataBroker should not be null", dataBroker);
175 itUtils = new OvsdbItUtils(dataBroker);
176 mdsalUtils = new MdsalUtils(dataBroker);
177 assertNotNull("mdsalUtils should not be null", mdsalUtils);
178 southboundUtils = new SouthboundUtils(mdsalUtils);
179 nvSouthboundUtils = new org.opendaylight.netvirt.it.SouthboundUtils(mdsalUtils);
180 assertTrue("Did not find " + NETVIRT_TOPOLOGY_ID, getNetvirtTopology());
181 flowITUtil = new FlowITUtil(dataBroker);
186 private void getProperties() {
187 Properties props = System.getProperties();
188 String addressStr = props.getProperty(NetvirtITConstants.SERVER_IPADDRESS);
189 String portStr = props.getProperty(NetvirtITConstants.SERVER_PORT, NetvirtITConstants.DEFAULT_SERVER_PORT);
190 String connectionType = props.getProperty(NetvirtITConstants.CONNECTION_TYPE, "active");
191 String controllerStr = props.getProperty(NetvirtITConstants.CONTROLLER_IPADDRESS, "0.0.0.0");
192 userSpaceEnabled = props.getProperty(NetvirtITConstants.USERSPACE_ENABLED, "no");
193 LOG.info("setUp: Using the following properties: mode= {}, ip:port= {}:{}, controller ip: {}, "
194 + "userspace.enabled: {}",
195 connectionType, addressStr, portStr, controllerStr, userSpaceEnabled);
198 private Boolean getNetvirtTopology() throws Exception {
199 LOG.info("getNetvirtTopology: looking for {}...", NETVIRT_TOPOLOGY_ID);
200 Boolean found = false;
201 TopologyId topologyId = new TopologyId(NETVIRT_TOPOLOGY_ID);
202 InstanceIdentifier<Topology> path =
203 InstanceIdentifier.create(NetworkTopology.class).child(Topology.class, new TopologyKey(topologyId));
204 final NotifyingDataChangeListener netvirtTopologyListener =
205 new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL,
206 NotifyingDataChangeListener.BIT_CREATE, path, null);
207 netvirtTopologyListener.registerDataChangeListener(dataBroker);
208 netvirtTopologyListener.waitForCreation(60000);
209 Topology topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, path);
210 if (topology != null) {
211 LOG.info("getNetvirtTopology: found {}...", NETVIRT_TOPOLOGY_ID);
214 netvirtTopologyListener.close();
219 @SuppressWarnings("checkstyle:IllegalCatch")
220 private void validateDefaultFlows(long datapathId, int timeout) {
221 LOG.info("Validating default flows");
222 for (DefaultFlow defaultFlow : DefaultFlow.values()) {
224 flowITUtil.verifyFlowByFields(datapathId, defaultFlow.getFlowId(), defaultFlow.getTableId(), timeout);
225 //flowITUtil.verifyFlowById(datapathId, defaultFlow.getFlowId(), defaultFlow.getTableId());
226 } catch (Exception e) {
227 LOG.error("Failed to verify flow id : {}", defaultFlow.getFlowId());
228 fail("Failed to verify flow id : " + defaultFlow.getFlowId());
233 private void addLocalIp(NodeInfo nodeInfo) {
234 Map<String, String> otherConfigs = Maps.newHashMap();
235 otherConfigs.put("local_ip", "10.1.1.1");
236 assertTrue(nvSouthboundUtils.addOpenVSwitchOtherConfig(nodeInfo.ovsdbNode, otherConfigs));
240 * Test for basic southbound events to netvirt.
241 * <pre>The test will:
242 * - connect to an OVSDB node and verify it is added to operational
243 * - then verify that br-int was created on the node and stored in operational
244 * - a port is then added to the bridge to verify that it is ignored by netvirt
245 * - remove the bridge
246 * - remove the node and verify it is not in operational
250 @SuppressWarnings("checkstyle:IllegalCatch")
251 public void testNetVirt() throws InterruptedException {
252 try (DockerOvs ovs = new DockerOvs()) {
253 ovs.logState(0, "idle");
254 ConnectionInfo connectionInfo =
255 SouthboundUtils.getConnectionInfo(ovs.getOvsdbAddress(0), ovs.getOvsdbPort(0));
256 NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
258 LOG.info("testNetVirt: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
259 addLocalIp(nodeInfo);
261 validateDefaultFlows(nodeInfo.datapathId, 2 * 60 * 1000);
262 ovs.logState(0, "default flows");
264 nodeInfo.disconnect();
265 } catch (Exception e) {
266 LOG.error("testNetVirt: Exception thrown by OvsDocker.OvsDocker()", e);
267 fail("testNetVirt: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());
273 * Test a basic neutron use case. This test constructs a Neutron network, subnet, and two "vm" ports
274 * and validates that the correct flows are installed on OVS. Then it pings from one VM port to the other.
275 * @throws InterruptedException if we're interrupted while waiting for some mdsal operation to complete
278 @SuppressWarnings("checkstyle:IllegalCatch")
279 public void testNeutronNet() throws InterruptedException {
280 LOG.warn("testNeutronNet: starting test");
281 try (DockerOvs ovs = new DockerOvs()) {
282 Neutron neutron = new Neutron(mdsalUtils);
284 Boolean isUserSpace = userSpaceEnabled.equals("yes");
285 LOG.info("isUserSpace: {}, usingExternalDocker: {}", isUserSpace, ovs.usingExternalDocker());
286 if (ovs.usingExternalDocker()) {
287 netOvs = new RealNetOvsImpl(ovs, isUserSpace, mdsalUtils, neutron, southboundUtils);
289 netOvs = new DockerNetOvsImpl(ovs, isUserSpace, mdsalUtils, neutron, southboundUtils);
292 netOvs.logState(0, "idle");
293 ConnectionInfo connectionInfo =
294 SouthboundUtils.getConnectionInfo(ovs.getOvsdbAddress(0), ovs.getOvsdbPort(0));
295 NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
297 LOG.info("testNeutronNet: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
298 addLocalIp(nodeInfo);
300 validateDefaultFlows(nodeInfo.datapathId, 2 * 60 * 1000);
301 netOvs.logState(0, "default flows");
303 neutron.createNetwork();
304 neutron.createSubnet();
306 String port1 = netOvs.createPort(nodeInfo.bridgeNode);
307 String port2 = netOvs.createPort(nodeInfo.bridgeNode);
309 InstanceIdentifier<TerminationPoint> tpIid =
310 southboundUtils.createTerminationPointInstanceIdentifier(nodeInfo.bridgeNode, port2);
311 final NotifyingDataChangeListener portOperationalListener =
312 new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL,
313 NotifyingDataChangeListener.BIT_CREATE, tpIid, null);
314 portOperationalListener.registerDataChangeListener(dataBroker);
316 netOvs.preparePortForPing(port1);
317 netOvs.preparePortForPing(port2);
319 portOperationalListener.waitForCreation(10000);
321 netOvs.logState(0, "after ports");
323 int rc = netOvs.ping(port1, port2);
324 netOvs.logState(0, "after ping");
325 assertEquals("Ping failed rc: " + rc, 0, rc);
328 nodeInfo.disconnect();
329 } catch (Exception e) {
330 LOG.error("testNeutronNet: Exception thrown by OvsDocker.OvsDocker()", e);
331 fail("testNeutronNet: Exception thrown by OvsDocker.OvsDocker() : " + e.getMessage());