<bundle>mvn:org.opendaylight.netvirt/utils.mdsal-utils/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netvirt/utils.netvirt-it-utils/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.netvirt/utils.neutron-utils/{{VERSION}}</bundle>
+ <bundle>mvn:org.opendaylight.ovsdb/utils.ovsdb-it-utils/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.ovsdb/utils.mdsal-utils/{{VERSION}}</bundle>
<bundle>mvn:org.opendaylight.ovsdb/utils.southbound-utils/{{VERSION}}</bundle>
</feature>
import org.opendaylight.netvirt.openstack.netvirt.providers.NetvirtProvidersProvider;
import org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.PipelineOrchestrator;
import org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.Service;
-import org.opendaylight.netvirt.utils.netvirt.it.utils.NetvirtItUtils;
-import org.opendaylight.netvirt.utils.netvirt.it.utils.NeutronNetItUtil;
-import org.opendaylight.netvirt.utils.neutron.utils.NeutronUtils;
-import org.opendaylight.ovsdb.lib.notation.Version;
import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronNetwork;
import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronPort;
import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
import org.opendaylight.netvirt.openstack.netvirt.translator.crud.INeutronPortCRUD;
import org.opendaylight.netvirt.openstack.netvirt.translator.crud.INeutronSecurityGroupCRUD;
import org.opendaylight.netvirt.openstack.netvirt.translator.crud.INeutronSecurityRuleCRUD;
+import org.opendaylight.netvirt.utils.netvirt.it.utils.NetvirtItUtils;
+import org.opendaylight.netvirt.utils.netvirt.it.utils.NeutronNetItUtil;
+import org.opendaylight.netvirt.utils.netvirt.it.utils.PingableNeutronNetItUtil;
+import org.opendaylight.netvirt.utils.neutron.utils.NeutronUtils;
+import org.opendaylight.ovsdb.lib.notation.Version;
import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
import org.opendaylight.ovsdb.utils.ovsdb.it.utils.DockerOvs;
import org.opendaylight.ovsdb.utils.ovsdb.it.utils.ItConstants;
/**
* Test a basic neutron use case. This test constructs a Neutron network, subnet, dhcp port, and two "vm" ports
- * and validates that the correct flows are installed on OVS.
+ * and validates that the correct flows are installed on OVS. Then it pings from one VM port to the other.
* @throws InterruptedException if we're interrupted while waiting for some mdsal operation to complete
*/
@Test
ConnectionInfo connectionInfo = SouthboundUtils.getConnectionInfo(ovs.getOvsdbAddress(0), ovs.getOvsdbPort(0));
NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
nodeInfo.connect();
- LOG.warn("testNeutronNet: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
// Create the objects
- NeutronNetItUtil net = new NeutronNetItUtil(southboundUtils, UUID.randomUUID().toString());
+ PingableNeutronNetItUtil net = new PingableNeutronNetItUtil(ovs, southboundUtils, UUID.randomUUID().toString());
net.create();
net.createPort(nodeInfo.bridgeNode, "dhcp", "network:dhcp");
net.createPort(nodeInfo.bridgeNode, "vm1");
net.createPort(nodeInfo.bridgeNode, "vm2");
+ Thread.sleep(3000);
// Check flows created for all ports
- for (int i = 1; i <= net.neutronPorts.size(); i++) {
- nvItUtils.verifyFlow(nodeInfo.datapathId, "DropFilter_" + i,
- pipelineOrchestrator.getTable(Service.CLASSIFIER));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "LocalMac_" + net.segId + "_" + i + "_" + net.macFor(i),
+ for (NeutronNetItUtil.PortInfo portInfo : net.portInfoByName.values()) {
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "DropFilter_" + portInfo.ofPort,
pipelineOrchestrator.getTable(Service.CLASSIFIER));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "ArpResponder_" + net.segId + "_" + net.ipFor(i),
- pipelineOrchestrator.getTable(Service.ARP_RESPONDER));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "UcastOut_" + net.segId + "_" + i + "_" + net.macFor(i),
- pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "LocalMac_" + net.segId + "_" + portInfo.ofPort
+ + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.CLASSIFIER));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "ArpResponder_" + net.segId + "_" + portInfo.ip,
+ pipelineOrchestrator.getTable(Service.ARP_RESPONDER));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "UcastOut_" + net.segId + "_" + portInfo.ofPort
+ + "_" + portInfo.mac, pipelineOrchestrator.getTable(Service.L2_FORWARDING));
}
- // Check flows created for vm ports only
- for (int i = 2; i <= net.neutronPorts.size(); i++) {
- nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_ARP_" + net.segId + "_" + i + "_",
+ for (NeutronNetItUtil.PortInfo portInfo : net.portInfoByName.values()) {
+ // Check flows created for vm ports only
+ if (portInfo.name.equals("dhcp")) {
+ continue;
+ }
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
pipelineOrchestrator.getTable(Service.INGRESS_ACL));
-
- nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_Allow_VM_IP_MAC_" + i + net.macFor(i) + "_Permit_",
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_Allow_VM_IP_MAC_" + portInfo.ofPort
+ + portInfo.mac + "_Permit_", pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_ARP_" + net.segId + "_" + portInfo.ofPort + "_",
pipelineOrchestrator.getTable(Service.EGRESS_ACL));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_ARP_" + net.segId + "_" + i + "_",
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCP_Server_" + portInfo.ofPort + "_DROP_",
pipelineOrchestrator.getTable(Service.EGRESS_ACL));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCP_Server_" + i + "_DROP_",
- pipelineOrchestrator.getTable(Service.EGRESS_ACL));
- nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCPv6_Server_" + i + "_DROP_",
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCPv6_Server_" + portInfo.ofPort + "_DROP_",
pipelineOrchestrator.getTable(Service.EGRESS_ACL));
}
//nvItUtils.verifyFlow(nodeInfo.datapathId, "TunnelMiss_" + net.segId,
// pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+
+ net.preparePortForPing("vm1");
+ net.preparePortForPing("vm2");
+ net.ping("vm1", "vm2");
+
net.destroy();
nodeInfo.disconnect();
} catch (Exception e) {
}
@Test
+ @Ignore //For now....fails every once in a while on connect. TODO: fix it
public void twoNodes() throws InterruptedException {
System.getProperties().setProperty(ItConstants.DOCKER_COMPOSE_FILE_NAME, "two_dockers-ovs-2.5.1.yml");
}
bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, ovsdbBridgeAugmentationBuilder.build());
- result = mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, bridgeIid, bridgeNodeBuilder.build());
+ Node node = bridgeNodeBuilder.build();
+ result = mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, bridgeIid, node);
LOG.info("addBridge: result: {}", result);
} else {
throw new InvalidParameterException("Could not find ConnectionInfo");
<artifactId>utils.mdsal-openflow</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.ovsdb</groupId>
+ <artifactId>utils.ovsdb-it-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.mdsal.model</groupId>
<artifactId>ietf-inet-types</artifactId>
FlowUtils.createNodeBuilder(datapathId);
FlowBuilder flowBuilder =
FlowUtils.initFlowBuilder(new FlowBuilder(), flowId, table);
+
InstanceIdentifier<Flow> iid = FlowUtils.createFlowPath(flowBuilder, nodeBuilder);
NotifyingDataChangeListener waitForIt = new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION,
package org.opendaylight.netvirt.utils.netvirt.it.utils;
+import java.io.IOException;
+import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
-import java.util.Vector;
import com.google.common.collect.Maps;
import org.junit.Assert;
public SouthboundUtils southboundUtils;
public NeutronUtils neutronUtils;
- public Vector<NeutronPort> neutronPorts = new Vector();
+
+ /**
+ * Information about a port created using createPort() - fields should be pretty self explanatory
+ */
+ public class PortInfo {
+ public PortInfo(String name, long ofPort) {
+ this.name = name;
+ this.ofPort = ofPort;
+ this.ip = ipFor(ofPort);
+ this.mac = macFor(ofPort);
+ }
+
+ public String name;
+ public NeutronPort neutronPort;
+ public String ip;
+ public String mac;
+ public long ofPort;
+ }
+
+ /**
+ * Maps port names (the ones you pass in to createPort() to their PortInfo objects
+ */
+ public Map<String, PortInfo> portInfoByName = new HashMap<String, PortInfo>();
/**
* Construct a new NeutronNetItUtil.
* @param tenantId tenant ID
*/
public NeutronNetItUtil(SouthboundUtils southboundUtils, String tenantId) {
- this(southboundUtils, tenantId, "101", "f7:00:00:0f:00:", "10.0.0.", "10.0.0.0/24");
+ this(southboundUtils, tenantId, "101", "f4:00:00:0f:00:", "10.0.0.", "10.0.0.0/24");
}
/**
* Clean up all created neutron objects.
*/
public void destroy() {
- for (NeutronPort port : neutronPorts) {
- neutronUtils.removeNeutronPort(port.getID());
+ for (PortInfo portInfo : portInfoByName.values()) {
+ neutronUtils.removeNeutronPort(portInfo.neutronPort.getID());
}
- neutronPorts.clear();
+ //TODO: probably more polite to clean up everything else as well...
+ //TODO: for now just assume that the docker image will be recreated
+ //TODO: before each test
+ portInfoByName.clear();
if (neutronSubnet != null) {
neutronUtils.removeNeutronSubnet(neutronSubnet.getID());
* @param portName name for this port
* @throws InterruptedException if we're interrupted while waiting for objects to be created
*/
- public void createPort(Node bridge, String portName) throws InterruptedException {
+ public void createPort(Node bridge, String portName) throws InterruptedException, IOException {
createPort(bridge, portName, "compute:None");
}
* @param secGroups Optional NeutronSecurityGroup objects see NeutronUtils.createNeutronSecurityGroup()
* @throws InterruptedException if we're interrupted while waiting for objects to be created
*/
- public void createPort(Node bridge, String portName, String owner, NeutronSecurityGroup... secGroups) throws InterruptedException {
- long idx = neutronPorts.size() + 1;
+ public void createPort(Node bridge, String portName, String owner, NeutronSecurityGroup... secGroups)
+ throws InterruptedException, IOException {
+ PortInfo portInfo = buildPortInfo(portName);
+ doCreatePort(bridge, portInfo, owner, "internal", secGroups);
+ }
+
+ protected PortInfo buildPortInfo(String portName) {
+ Assert.assertFalse("Can't have two ports with the same name", portInfoByName.containsKey(portName));
+
+ long idx = portInfoByName.size() + 1;
Assert.assertTrue(idx < 256);
- String mac = macFor(idx);
- String ip = ipFor(idx);
+ return new PortInfo(portName, idx);
+ }
+
+ protected void doCreatePort(Node bridge, PortInfo portInfo, String owner,
+ String portType, NeutronSecurityGroup ... secGroups) throws InterruptedException {
+
String portId = UUID.randomUUID().toString();
- neutronPorts.add(neutronUtils.createNeutronPort(id, subnetId, portId, owner, ip, mac, secGroups));
+ portInfo.neutronPort = neutronUtils.createNeutronPort(
+ id, subnetId, portId, owner, portInfo.ip, portInfo.mac, secGroups);
//TBD: Use NotifyingDataChangeListener
Thread.sleep(1000);
Map<String, String> externalIds = Maps.newHashMap();
- externalIds.put("attached-mac", mac);
+ externalIds.put("attached-mac", portInfo.mac);
externalIds.put("iface-id", portId);
- southboundUtils.addTerminationPoint(bridge, portName, "internal", null, externalIds, idx);
+ southboundUtils.addTerminationPoint(bridge, portInfo.name, portType, null, externalIds, portInfo.ofPort);
+
+ portInfoByName.put(portInfo.name, portInfo);
}
/**
* @return the mac address
*/
public String macFor(long portNum) {
- return macPfx + String.format("%02x", portNum);
+ return macPfx + String.format("%02x", 5 - portNum);
}
/**
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.utils.netvirt.it.utils;
+
+import org.junit.Assert;
+import org.opendaylight.netvirt.openstack.netvirt.translator.NeutronSecurityGroup;
+import org.opendaylight.ovsdb.utils.ovsdb.it.utils.DockerOvs;
+import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+
+import java.io.IOException;
+
+/**
+ * This class adds the ability to issue pings from one port to another to its base class, NeutronNetItUtil.
+ * Please see NetvirtIT#testNeutronNet for an example of how this class is used
+ * N.B. At this point this class only supports DockerOvs runs where the docker-compose file runs a single
+ * docker container with OVS. A little work will need to be invested to extend this functionality to support
+ * configuring "ping-ability" on multiple docker containers.
+ */
+public class PingableNeutronNetItUtil extends NeutronNetItUtil {
+
+ private static final int DEFAULT_WAIT = 5000;
+ private DockerOvs dockerOvs;
+
+ public PingableNeutronNetItUtil(DockerOvs dockerOvs, SouthboundUtils southboundUtils, String tenantId) {
+ super(southboundUtils, tenantId);
+ this.dockerOvs = dockerOvs;
+ }
+
+ public PingableNeutronNetItUtil(DockerOvs dockerOvs, SouthboundUtils southboundUtils, String tenantId,
+ String segId, String macPfx, String ipPfx, String cidr) {
+ super(southboundUtils, tenantId, segId, macPfx, ipPfx, cidr);
+ this.dockerOvs = dockerOvs;
+ }
+
+ /**
+ * Create a port on the network. The deviceOwner will be set to "compute:None".
+ * @param bridge bridge where the port will be created on OVS
+ * @param portName name for this port
+ * @param owner deviceOwner, e.g., "network:dhcp"
+ * @param secGroups Optional NeutronSecurityGroup objects see NeutronUtils.createNeutronSecurityGroup()
+ * @throws InterruptedException if we're interrupted while waiting for objects to be created
+ */
+ public void createPort(Node bridge, String portName, String owner, NeutronSecurityGroup... secGroups)
+ throws InterruptedException, IOException {
+
+ PortInfo portInfo = buildPortInfo(portName);
+
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "tuntap", "add", portInfo.name, "mode", "tap");
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "link", "set", "dev", portInfo.name, "address", portInfo.mac);
+
+ doCreatePort(bridge, portInfo, owner, "tap", secGroups);
+ }
+
+ /**
+ * This method must be run on a port before calling ping() or pingIp()
+ * @param portName The name of the port used when it was created using createPort()
+ * @throws IOException if an IO error occurs with one of the spawned procs
+ * @throws InterruptedException because we sleep
+ */
+ public void preparePortForPing(String portName) throws IOException, InterruptedException {
+ String nsName = "ns-" + portName;
+
+ PortInfo portInfo = portInfoByName.get(portName);
+ Assert.assertNotNull(portInfo);
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "add", nsName);
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "link", "set", portName, "netns", nsName);
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "addr",
+ "add", "dev", portName, portInfo.ip + "/24");
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "link",
+ "set", "dev", portName, "up");
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", nsName, "ip", "route",
+ "add", "default", "via", portInfo.ip);
+ }
+
+ /**
+ * Ping from one port to the other
+ * @param fromPort name of the port to ping from. This is the name you used for createPort.
+ * @param toPort name of the port to ping to. This is the name you used for createPort.
+ * @throws IOException if an IO error occurs with one of the spawned procs
+ * @throws InterruptedException because we sleep
+ */
+ public void ping(String fromPort, String toPort) throws IOException, InterruptedException {
+ PortInfo portInfo = portInfoByName.get(toPort);
+ Assert.assertNotNull(portInfo);
+ pingIp(fromPort, portInfo.ip);
+ }
+
+ /**
+ * Ping from one port to an IP address
+ * @param fromPort name of the port to ping from. This is the name you used for createPort.
+ * @param ip The IP address to ping
+ * @throws IOException if an IO error occurs with one of the spawned procs
+ * @throws InterruptedException because we sleep
+ */
+ public void pingIp(String fromPort, String ip) throws IOException, InterruptedException {
+ String fromNs = "ns-" + fromPort;
+ dockerOvs.runInContainer(DEFAULT_WAIT, 0, "ip", "netns", "exec", fromNs, "ping", "-c", "4", ip);
+ }
+}