import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Assert;
import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
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.neutron.spi.INeutronPortCRUD;
import org.opendaylight.neutron.spi.INeutronSecurityGroupCRUD;
import org.opendaylight.netvirt.openstack.netvirt.providers.openflow13.Service;
import org.opendaylight.ovsdb.utils.ovsdb.it.utils.OvsdbItUtils;
import org.opendaylight.ovsdb.utils.ovsdb.it.utils.NodeInfo;
-import org.opendaylight.netvirt.utils.mdsal.openflow.FlowUtils;
import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
nodeInfo.connect();
LOG.info("testNetVirtFixedSG: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
+ //TBD: This should be a utility function
// Verify the minimum version required for this test
OvsdbNodeAugmentation ovsdbNodeAugmentation = nodeInfo.ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
Assert.assertNotNull(ovsdbNodeAugmentation);
return;
}
+ //TBD: Use NeutronNetItUtil
NeutronNetwork nn = neutronUtils.createNeutronNetwork(networkId, tenantId,
NetworkHandler.NETWORK_TYPE_VXLAN, "100");
NeutronSubnet ns = neutronUtils.createNeutronSubnet(subnetId, tenantId, networkId, "10.0.0.0/24");
testDefaultSG(nport, nodeInfo.datapathId, nn, tenantId, portId);
Thread.sleep(1000);
+ assertTrue(neutronUtils.removeNeutronPort(dhcp.getID()));
+ assertTrue(neutronUtils.removeNeutronPort(nport.getID()));
+ assertTrue(neutronUtils.removeNeutronSubnet(ns.getID()));
+ assertTrue(neutronUtils.removeNeutronNetwork(nn.getID()));
+
nodeInfo.disconnect();
}
flowId = "Ingress_IP" + nn.getProviderSegmentationID() + "_" + nport.getMacAddress() + "_Permit_";
nvItUtils.verifyFlow(datapathId, flowId, pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+
+ ineutronSecurityGroupCRUD.remove(neutronSG.getID());
+ ineutronSecurityRuleCRUD.removeNeutronSecurityRule(nsrEG.getID());
+ ineutronSecurityRuleCRUD.removeNeutronSecurityRule(nsrIN.getID());
}
- private Flow getFlow (
- FlowBuilder flowBuilder,
- org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder nodeBuilder,
- LogicalDatastoreType store) throws InterruptedException {
-
- Flow flow = null;
- for (int i = 0; i < 10; i++) {
- LOG.info("getFlow try {} from {}: looking for flow: {}, node: {}",
- i, store, flowBuilder.build(), nodeBuilder.build());
- flow = FlowUtils.getFlow(flowBuilder, nodeBuilder, dataBroker.newReadOnlyTransaction(), store);
- if (flow != null) {
- LOG.info("getFlow try {} from {}: found flow: {}", i, store, flow);
- break;
- }
- Thread.sleep(1000);
+ /**
+ * 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.
+ * @throws InterruptedException if we're interrupted while waiting for some mdsal operation to complete
+ */
+ @Test
+ public void testNeutronNet() throws InterruptedException {
+ LOG.warn("testNetWithTwoVms: starting test");
+ ConnectionInfo connectionInfo = SouthboundUtils.getConnectionInfo(addressStr, portStr);
+ NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
+ nodeInfo.connect();
+ LOG.warn("testNetWithTwoVms: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
+
+ // Create the objects
+ NeutronNetItUtil net = new NeutronNetItUtil(southboundUtils, UUID.randomUUID().toString());
+ net.create();
+ net.createPort(nodeInfo.bridgeNode, "dhcp", "network:dhcp");
+ net.createPort(nodeInfo.bridgeNode, "vm1");
+ net.createPort(nodeInfo.bridgeNode, "vm2");
+
+
+ // 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),
+ 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));
+ }
+
+ // 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 + "_",
+ pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_Allow_VM_IP_MAC_" + i + net.macFor(i) + "_Permit_",
+ pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_ARP_" + net.segId + "_" + i + "_",
+ 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_",
+ pipelineOrchestrator.getTable(Service.EGRESS_ACL));
}
- return flow;
+
+ // Check ingress/egress acl flows for DHCP
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCP_Client_Permit_",
+ pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Egress_DHCPv6_Client_Permit_",
+ pipelineOrchestrator.getTable(Service.EGRESS_ACL));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_DHCPv6_Server" + net.segId + "_"
+ + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "Ingress_DHCP_Server" + net.segId + "_"
+ + net.macFor(1) + "_Permit_", pipelineOrchestrator.getTable(Service.INGRESS_ACL));
+
+ // Check l2 broadcast flows
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "TunnelFloodOut_" + net.segId,
+ pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+ nvItUtils.verifyFlow(nodeInfo.datapathId, "BcastOut_" + net.segId,
+ pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+
+ //TBD Figure out why this does not work:
+ //nvItUtils.verifyFlow(nodeInfo.datapathId, "TunnelMiss_" + net.segId,
+ // pipelineOrchestrator.getTable(Service.L2_FORWARDING));
+
+ net.destroy();
+ nodeInfo.disconnect();
}
+
}
if (neutronPort.getDeviceOwner().contains("dhcp")) {
return neutronPort;
}
- /* if the current port is a DHCP port, return the same*/
- if (neutronPort.getDeviceOwner().contains("dhcp")) {
- return neutronPort;
- }
/*Since all the fixed ip assigned to a port should be
*from the same network, first port is sufficient.*/
List<Neutron_IPs> fixedIps = neutronPort.getFixedIPs();
.child(Flow.class, flowBuilder.getKey()).build();
}
+ public static InstanceIdentifier<Table> createTablePath(NodeBuilder nodeBuilder, short table) {
+ return InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, nodeBuilder.getKey())
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(table)).build();
+ }
+
public static InstanceIdentifier<Node> createNodePath(NodeBuilder nodeBuilder) {
return InstanceIdentifier.builder(Nodes.class).child(Node.class, nodeBuilder.getKey()).build();
}
return null;
}
+ public static Table getTable(NodeBuilder nodeBuilder, short table,
+ ReadOnlyTransaction readTx, final LogicalDatastoreType store) {
+ try {
+ Optional<Table> data = readTx.read(store, createTablePath(nodeBuilder, table)).get();
+ if (data.isPresent()) {
+ return data.get();
+ }
+ } catch (InterruptedException|ExecutionException e) {
+ LOG.error("Failed to get table {}", table, e);
+ }
+
+ LOG.info("Cannot find data for table {} in {}", table, store);
+ return null;
+ }
+
public static FlowBuilder getPipelineFlow(short table, short gotoTable) {
FlowBuilder flowBuilder = new FlowBuilder();
flowBuilder.setMatch(new MatchBuilder().build());
<artifactId>utils.mdsal-utils</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>utils.neutron-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.ovsdb</groupId>
- <artifactId>utils.mdsal-utils</artifactId>
+ <artifactId>utils.mdsal-utils</artifactId>
<version>${ovsdb.version}</version>
</dependency>
<dependency>
import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
flow);
}
+ /**
+ * Log the flows in a given table.
+ * @param datapathId dpid
+ * @param tableNum table number
+ * @param store configuration or operational
+ */
+ public void logFlows(long datapathId, short tableNum, LogicalDatastoreType store) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder nodeBuilder =
+ FlowUtils.createNodeBuilder(datapathId);
+ Table table = FlowUtils.getTable(nodeBuilder, tableNum, dataBroker.newReadOnlyTransaction(), store);
+ if (table == null) {
+ LOG.info("logFlows: Could not find table {} in {}", tableNum, store);
+ }
+ //TBD: Log table and store in one line, flows in following lines
+ for (Flow flow : table.getFlow()) {
+ LOG.info("logFlows: table {} flow {} in {}", tableNum, flow.getFlowName(), store);
+ }
+ }
+
+ /**
+ * Log the flows in a given table.
+ * @param datapathId dpid
+ * @param table table number
+ */
+ public void logFlows(long datapathId, short table) {
+ logFlows(datapathId, table, LogicalDatastoreType.CONFIGURATION);
+ }
+
/**
* Get a DataBroker and assert that it is not null.
* @param providerContext ProviderContext from which to retrieve the DataBroker
--- /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 java.util.Map;
+import java.util.UUID;
+import java.util.Vector;
+
+import com.google.common.collect.Maps;
+import org.junit.Assert;
+import org.opendaylight.netvirt.utils.neutron.utils.NeutronUtils;
+import org.opendaylight.neutron.spi.NeutronNetwork;
+import org.opendaylight.neutron.spi.NeutronPort;
+import org.opendaylight.neutron.spi.NeutronSubnet;
+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;
+
+/**
+ * A utility class used in integration tests that need to create neutron networks with some ports.
+ * Please see NetvirtIT#testNeutronNet for an example of how this class is used
+ */
+public class NeutronNetItUtil {
+
+ public final String tenantId;
+ public final String id;
+ public final String subnetId;
+ public NeutronNetwork neutronNetwork;
+ public NeutronSubnet neutronSubnet;
+ public String segId = "100";
+ public String macPfx;
+ public String ipPfx;
+ public String cidr;
+
+ public SouthboundUtils southboundUtils;
+ public NeutronUtils neutronUtils;
+ public Vector<NeutronPort> neutronPorts = new Vector();
+
+ /**
+ * Construct a new NeutronNetItUtil.
+ * @param southboundUtils used to create termination points
+ * @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");
+ }
+
+ /**
+ * Construct a new NeutronNetItUtil.
+ * @param southboundUtils used to create termination points
+ * @param tenantId tenant ID
+ * @param segId the segmentation id to use for the neutron network
+ * @param macPfx the first characters of the mac addresses generated for ports. Format is "f7:00:00:0f:00:"
+ * @param ipPfx the first characters of the ip addresses generated for ports. Format is "10.0.0."
+ * @param cidr the cidr for this network, e.g., "10.0.0.0/24"
+ */
+ public NeutronNetItUtil(SouthboundUtils southboundUtils, String tenantId,
+ String segId, String macPfx, String ipPfx, String cidr) {
+ this.tenantId = tenantId;
+ this.segId = segId;
+ this.macPfx = macPfx;
+ this.ipPfx = ipPfx;
+ this.cidr = cidr;
+
+ this.id = UUID.randomUUID().toString();
+ this.subnetId = UUID.randomUUID().toString();
+
+ this.southboundUtils = southboundUtils;
+ neutronUtils = new NeutronUtils();
+ neutronNetwork = null;
+ neutronSubnet = null;
+ }
+
+ /**
+ * Create the network and subnet.
+ */
+ public void create() {
+ neutronNetwork = neutronUtils.createNeutronNetwork(id, tenantId, "vxlan", segId);
+ neutronSubnet = neutronUtils.createNeutronSubnet(subnetId, tenantId, id, "10.0.0.0/24");
+ }
+
+ /**
+ * Clean up all created neutron objects.
+ */
+ public void destroy() {
+ for (NeutronPort port : neutronPorts) {
+ neutronUtils.removeNeutronPort(port.getID());
+ }
+ neutronPorts.clear();
+
+ if (neutronSubnet != null) {
+ neutronUtils.removeNeutronSubnet(neutronSubnet.getID());
+ neutronSubnet = null;
+ }
+
+ if (neutronNetwork != null) {
+ neutronUtils.removeNeutronNetwork(neutronNetwork.getID());
+ neutronNetwork = null;
+ }
+ }
+
+ /**
+ * 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
+ * @throws InterruptedException if we're interrupted while waiting for objects to be created
+ */
+ public void createPort(Node bridge, String portName) throws InterruptedException {
+ createPort(bridge, portName, "compute:None");
+ }
+
+ /**
+ * 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"
+ * @throws InterruptedException if we're interrupted while waiting for objects to be created
+ */
+ public void createPort(Node bridge, String portName, String owner) throws InterruptedException {
+ long idx = neutronPorts.size() + 1;
+ Assert.assertTrue(idx < 256);
+ String mac = macFor(idx);
+ String ip = ipFor(idx);
+ String portId = UUID.randomUUID().toString();
+ neutronPorts.add(neutronUtils.createNeutronPort(id, subnetId, portId, owner, ip, mac));
+
+ //TBD: Use NotifyingDataChangeListener
+ Thread.sleep(1000);
+
+ Map<String, String> externalIds = Maps.newHashMap();
+ externalIds.put("attached-mac", mac);
+ externalIds.put("iface-id", portId);
+ southboundUtils.addTerminationPoint(bridge, portName, "internal", null, externalIds, idx);
+ }
+
+ /**
+ * Get the mac address for the n'th port created on this network (starts at 1).
+ * @param portNum index of port created
+ * @return the mac address
+ */
+ public String macFor(long portNum) {
+ return macPfx + String.format("%02x", portNum);
+ }
+
+ /**
+ * Get the ip address for the n'th port created on this network (starts at 1).
+ * @param portNum index of port created
+ * @return the mac address
+ */
+ public String ipFor(long portNum) {
+ return ipPfx + portNum;
+ }
+}
+
return np;
}
+ public boolean removeNeutronPort(String uuid) {
+ INeutronPortCRUD iNeutronPortCRUD =
+ (INeutronPortCRUD) ServiceHelper.getGlobalInstance(INeutronPortCRUD.class, this);
+ return iNeutronPortCRUD.removePort(uuid);
+ }
+
public NeutronSubnet createNeutronSubnet(String subnetId, String tenantId,
String networkId, String cidr) {
INeutronSubnetCRUD iNeutronSubnetCRUD =
return ns;
}
+ public boolean removeNeutronSubnet(String uuid) {
+ INeutronSubnetCRUD iNeutronSubnetCRUD =
+ (INeutronSubnetCRUD) ServiceHelper.getGlobalInstance(INeutronSubnetCRUD.class, this);
+ return iNeutronSubnetCRUD.removeSubnet(uuid);
+ }
+
public NeutronNetwork createNeutronNetwork(String uuid, String tenantID, String networkTypeVxlan, String segId) {
INeutronNetworkCRUD iNeutronNetworkCRUD =
(INeutronNetworkCRUD) ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
iNeutronNetworkCRUD.addNetwork(nn);
return nn;
}
+
+ public boolean removeNeutronNetwork(String uuid) {
+ INeutronNetworkCRUD iNeutronNetworkCRUD =
+ (INeutronNetworkCRUD) ServiceHelper.getGlobalInstance(INeutronNetworkCRUD.class, this);
+ return iNeutronNetworkCRUD.removeNetwork(uuid);
+
+ }
}