[ONOS-7752] check north-south reachability for troubleshoot purpose
Change-Id: I973047c0fcd4fb241ad010fbbef9d016510b8deb
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
index f5ebe11..7dcbd32 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
@@ -66,4 +66,12 @@
* @return set of instance ports; empty list if no port exists
*/
Set<InstancePort> instancePorts(String osNetId);
+
+ /**
+ * Returns the floating IP with the supplied instance port.
+ *
+ * @param osPortId openstack port id
+ * @return openstack floating IP
+ */
+ IpAddress floatingIp(String osPortId);
}
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
index 71fc440..71d9bb1 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
@@ -156,7 +156,7 @@
String networkType(String netId);
/**
- * Returns gateway ip address upplied port ID.
+ * Returns gateway ip address with supplied port ID.
*
* @param portId openstack port id
* @return gateway ip address
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
index a1a8d30..18bf967 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
@@ -68,6 +68,7 @@
/**
* Returns the floating IP with the supplied floating IP ID.
+ *
* @param floatingIpId floating ip id
* @return openstack floating ip
*/
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
index 3f204fb..645a86d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
@@ -45,6 +45,7 @@
import org.onosproject.openstacknetworking.api.InstancePortService;
import org.onosproject.openstacknetworking.api.InstancePortStore;
import org.onosproject.openstacknetworking.api.InstancePortStoreDelegate;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
import org.slf4j.Logger;
import java.util.Objects;
@@ -101,6 +102,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterService routerService;
+
private final InstancePortStoreDelegate
delegate = new InternalInstancePortStoreDelegate();
private final InternalHostListener
@@ -224,6 +228,17 @@
return ImmutableSet.copyOf(ports);
}
+ @Override
+ public IpAddress floatingIp(String osPortId) {
+ checkNotNull(osPortId, ERR_NULL_INSTANCE_PORT_ID);
+
+ return routerService.floatingIps().stream()
+ .filter(fip -> osPortId.equals(fip.getPortId()))
+ .filter(fip -> fip.getFloatingIpAddress() != null)
+ .map(fip -> IpAddress.valueOf(fip.getFloatingIpAddress()))
+ .findFirst().orElse(null);
+ }
+
private boolean isInstancePortInUse(String portId) {
// TODO add checking logic
return false;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
index b8eb339..764cefe 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -250,7 +250,7 @@
return osRouter.getExternalGatewayInfo();
}
- private void handleEchoReply(IPv4 ipPacket, ICMP icmp) {
+ private boolean handleEchoReply(IPv4 ipPacket, ICMP icmp) {
String icmpInfoKey = icmpInfoKey(icmp,
IPv4.fromIPv4Address(ipPacket.getDestinationAddress()),
IPv4.fromIPv4Address(ipPacket.getSourceAddress()));
@@ -259,8 +259,10 @@
if (icmpInfoMap.get(icmpInfoKey) != null) {
processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey).value());
icmpInfoMap.remove(icmpInfoKey);
+ return true;
} else {
- log.warn("No ICMP Info for ICMP packet");
+ log.debug("No ICMP Info for ICMP packet");
+ return false;
}
}
@@ -472,8 +474,9 @@
context.block();
break;
case TYPE_ECHO_REPLY:
- handleEchoReply(ipPacket, icmp);
- context.block();
+ if (handleEchoReply(ipPacket, icmp)) {
+ context.block();
+ }
break;
default:
break;
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
index 885c7d8..b0efdfa 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
@@ -54,6 +54,11 @@
}
@Override
+ public IpAddress floatingIp(String osPortId) {
+ return null;
+ }
+
+ @Override
public void addListener(InstancePortListener listener) {
}
diff --git a/apps/openstacktroubleshoot/api/pom.xml b/apps/openstacktroubleshoot/api/pom.xml
index 2b5b420..d79df17 100644
--- a/apps/openstacktroubleshoot/api/pom.xml
+++ b/apps/openstacktroubleshoot/api/pom.xml
@@ -35,5 +35,11 @@
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps-openstacknetworking-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java b/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
index 272e257..fc1e735 100644
--- a/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
+++ b/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
@@ -15,22 +15,28 @@
*/
package org.onosproject.openstacktroubleshoot.api;
-import org.onlab.packet.IpAddress;
import org.onosproject.openstacknetworking.api.InstancePort;
-import java.util.Map;
-
/**
* Openstack troubleshoot interface.
*/
public interface OpenstackTroubleshootService {
/**
- * Checks all east-west VMs' connectivity.
- *
- * @return reachability map
+ * Troubleshoot traffic direction.
*/
- Map<String, Reachability> probeEastWestBulk();
+ enum Direction {
+
+ /**
+ * Signifies that EAST_WEST troubleshoot case.
+ */
+ EAST_WEST,
+
+ /**
+ * Signifies that NORTH_SOUTH troubleshoot case.
+ */
+ NORTH_SOUTH,
+ }
/**
* Checks a single VM-to-Vm connectivity.
@@ -43,18 +49,10 @@
InstancePort dstInstancePort);
/**
- * Checks all north-south router to VMs' connectivity.
- *
- * @return reachability map
- */
- Map<String, Reachability> probeNorthSouth();
-
- /**
* Checks a single router-to-VM connectivity.
*
- * @param netId network ID
- * @param ip destination VM IP address
+ * @param dstInstancePort destination instance port
* @return reachability
*/
- Reachability probeNorthSouth(String netId, IpAddress ip);
+ Reachability probeNorthSouth(InstancePort dstInstancePort);
}
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveFloatingIpCompleter.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveFloatingIpCompleter.java
new file mode 100644
index 0000000..4687209
--- /dev/null
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveFloatingIpCompleter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacktroubleshoot.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * Active VM floating IP address completer.
+ */
+public class ActiveFloatingIpCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ InstancePortService service = AbstractShellCommand.get(InstancePortService.class);
+ Set<String> set = service.instancePorts().stream()
+ .filter(p -> p.state() == InstancePort.State.ACTIVE)
+ .filter(p -> service.floatingIp(p.portId()) != null)
+ .map(p -> service.floatingIp(p.portId()).toString())
+ .collect(Collectors.toSet());
+
+ SortedSet<String> strings = delegate.getStrings();
+ strings.addAll(set);
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
index 696cb11..0b832da 100644
--- a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
@@ -26,11 +26,12 @@
import org.onosproject.openstacktroubleshoot.api.OpenstackTroubleshootService;
import org.onosproject.openstacktroubleshoot.api.Reachability;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
+
/**
* Checks the east-west VMs connectivity.
*/
@@ -57,8 +58,7 @@
OpenstackTroubleshootService tsService =
get(OpenstackTroubleshootService.class);
- InstancePortService instPortService =
- get(InstancePortService.class);
+ InstancePortService instPortService = get(InstancePortService.class);
if (tsService == null) {
error("Failed to troubleshoot openstack networking.");
@@ -72,15 +72,28 @@
if (isAll) {
printHeader();
- Map<String, Reachability> map = tsService.probeEastWestBulk();
- map.values().forEach(this::printReachability);
+ // send ICMP PACKET_OUT to all connect VMs whose instance port state is ACTIVE
+ Set<InstancePort> activePorts = instPortService.instancePorts().stream()
+ .filter(p -> p.state() == ACTIVE)
+ .collect(Collectors.toSet());
+
+ activePorts.forEach(srcPort ->
+ activePorts.forEach(dstPort ->
+ printReachability(tsService.probeEastWest(srcPort, dstPort))
+ )
+ );
} else {
if (vmIps.length > 2) {
print("Too many VM IPs. The number of IP should be limited to 2.");
return;
}
- IpAddress srcIp = IpAddress.valueOf(vmIps[0]);
+ IpAddress srcIp = getIpAddress(vmIps[0]);
+
+ if (srcIp == null) {
+ return;
+ }
+
InstancePort srcPort = instPort(instPortService, srcIp);
if (srcPort == null) {
@@ -91,7 +104,13 @@
final Set<IpAddress> dstIps = Sets.newConcurrentHashSet();
if (vmIps.length == 2) {
- dstIps.add(IpAddress.valueOf(vmIps[1]));
+ IpAddress dstIp = getIpAddress(vmIps[1]);
+
+ if (dstIp == null) {
+ return;
+ }
+
+ dstIps.add(dstIp);
}
if (vmIps.length == 1) {
@@ -130,4 +149,13 @@
String result = r.isReachable() ? REACHABLE : UNREACHABLE;
print(FORMAT, r.srcIp().toString(), ARROW, r.dstIp().toString(), result);
}
+
+ private IpAddress getIpAddress(String ipString) {
+ try {
+ return IpAddress.valueOf(vmIps[0]);
+ } catch (IllegalArgumentException e) {
+ error("Invalid IP address string.");
+ return null;
+ }
+ }
}
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackNorthSouthProbeCommand.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackNorthSouthProbeCommand.java
new file mode 100644
index 0000000..77c24bb
--- /dev/null
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackNorthSouthProbeCommand.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacktroubleshoot.cli;
+
+import com.google.common.collect.Sets;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.openstacktroubleshoot.api.OpenstackTroubleshootService;
+import org.onosproject.openstacktroubleshoot.api.Reachability;
+
+import java.util.Set;
+
+import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
+
+/**
+ * Checks the north-south VM connectivity.
+ */
+@Command(scope = "onos", name = "openstack-check-north-south",
+ description = "Checks the north-south VMs connectivity")
+public class OpenstackNorthSouthProbeCommand extends AbstractShellCommand {
+
+ private static final String REACHABLE = "Reachable :)";
+ private static final String UNREACHABLE = "Unreachable :(";
+ private static final String ARROW = "->";
+
+ private static final String FORMAT = "%-20s%-5s%-20s%-20s";
+
+ @Option(name = "-a", aliases = "--all", description = "Apply this command to all VMs",
+ required = false, multiValued = false)
+ private boolean isAll = false;
+
+ @Argument(index = 0, name = "vmIps", description = "VMs' IP addresses",
+ required = false, multiValued = true)
+ private String[] vmIps = null;
+
+ @Override
+ protected void execute() {
+ OpenstackTroubleshootService tsService = get(OpenstackTroubleshootService.class);
+ InstancePortService instPortService = get(InstancePortService.class);
+ OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
+ MastershipService mastershipService = get(MastershipService.class);
+ ClusterService clusterService = get(ClusterService.class);
+
+ if (tsService == null || osNodeService == null ||
+ instPortService == null || mastershipService == null) {
+ error("Failed to troubleshoot openstack networking.");
+ return;
+ }
+
+ if ((!isAll && vmIps == null) || (isAll && vmIps != null)) {
+ print("Please specify one of VM IP address or -a option.");
+ return;
+ }
+
+ NodeId localNodeId = clusterService.getLocalNode().id();
+
+ for (OpenstackNode gw : osNodeService.completeNodes(GATEWAY)) {
+ if (!localNodeId.equals(mastershipService.getMasterFor(gw.intgBridge()))) {
+ error("Current node is not the master for all gateway nodes. Please enforce mastership first!");
+ return;
+ }
+ }
+
+ if (isAll) {
+ printHeader();
+
+ // send ICMP PACKET_OUT to all connect VMs whose instance port state is ACTIVE
+ instPortService.instancePorts().stream()
+ .filter(p -> p.state() == ACTIVE)
+ .filter(p -> instPortService.floatingIp(p.portId()) != null)
+ .forEach(port -> printReachability(tsService.probeNorthSouth(port)));
+ } else {
+
+ final Set<InstancePort> ports = Sets.newConcurrentHashSet();
+
+ for (String ip : vmIps) {
+ instPortService.instancePorts().stream()
+ .filter(p -> p.state().equals(InstancePort.State.ACTIVE))
+ .filter(p -> instPortService.floatingIp(p.portId()) != null)
+ .filter(p -> ip.equals(instPortService.floatingIp(p.portId()).toString()))
+ .forEach(ports::add);
+ }
+
+ printHeader();
+ ports.forEach(port -> printReachability(tsService.probeNorthSouth(port)));
+ }
+ }
+
+ private void printHeader() {
+ print(FORMAT, "Source IP", "", "Destination IP", "Reachability");
+ }
+
+ private void printReachability(Reachability r) {
+ if (r == null) {
+ return;
+ }
+ String result = r.isReachable() ? REACHABLE : UNREACHABLE;
+ print(FORMAT, r.srcIp().toString(), ARROW, r.dstIp().toString(), result);
+ }
+}
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
index 2354076..13e9579 100644
--- a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
@@ -29,6 +29,7 @@
import org.onlab.packet.IPv4;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
import org.onlab.util.KryoNamespace;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipService;
@@ -36,6 +37,7 @@
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowEntry;
@@ -53,6 +55,7 @@
import org.onosproject.openstacknetworking.api.InstancePortService;
import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.api.OpenstackNode;
import org.onosproject.openstacknode.api.OpenstackNodeService;
import org.onosproject.openstacktroubleshoot.api.OpenstackTroubleshootService;
import org.onosproject.openstacktroubleshoot.api.Reachability;
@@ -65,12 +68,11 @@
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
-import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
@@ -83,13 +85,15 @@
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
+import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_PROBE_RULE;
import static org.onosproject.openstacknetworking.api.Constants.VTAG_TABLE;
import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
-import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
import static org.onosproject.openstacktroubleshoot.util.OpenstackTroubleshootUtil.getSegId;
/**
@@ -111,6 +115,8 @@
private static final int PREFIX_LENGTH = 32;
private static final int ICMP_PROCESSOR_PRIORITY = 99;
+ private static final MacAddress LOCAL_MAC = MacAddress.valueOf("11:22:33:44:55:66");
+
private static final String ICMP_COUNTER_NAME = "icmp-id-counter";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -195,68 +201,6 @@
}
@Override
- public Map<String, Reachability> probeEastWestBulk() {
-
- // install flow rules to enforce ICMP_REQUEST to be tagged and direct to ACL table
- eventExecutor.execute(() -> setAllVidTagRule(true));
-
- // install flow rules to enforce forwarding ICMP_REPLY to controller
- eventExecutor.execute(() -> setAllIcmpReplyRule(true));
-
- icmpReachabilityMap.clear();
-
- // send ICMP PACKET_OUT to all connect VMs whose instance port state is ACTIVE
- Set<InstancePort> activePorts = instancePortService.instancePorts().stream()
- .filter(p -> p.state() == ACTIVE)
- .collect(Collectors.toSet());
-
- timeoutSupplier(activePorts.size(), VID_TAG_RULE_INSTALL_TIMEOUT_MS, this::checkAllVidTagRules);
- timeoutSupplier(activePorts.size(), ICMP_RULE_INSTALL_TIMEOUT_MS, this::checkAllIcmpReplyRules);
-
- for (InstancePort srcPort : activePorts) {
-
- // we only let the master of the switch where the source host
- // is attached to send out ICMP request packet
- if (!mastershipService.isLocalMaster(srcPort.deviceId())) {
- continue;
- }
-
- for (InstancePort dstPort : activePorts) {
- // if the source and destination ports are identical, we do
- // not probe the reachability
- if (srcPort.equals(dstPort)) {
- continue;
- }
-
- // if the two ports are located in different types of networks,
- // we do not probe the reachability
- if (!osNetworkService.networkType(srcPort.networkId())
- .equals(osNetworkService.networkType(dstPort.networkId()))) {
- continue;
- }
-
- sendIcmpEchoRequest(srcPort, dstPort);
- }
- }
-
- long count = icmpReachabilityMap.asJavaMap().values().stream()
- .filter(r -> !r.isReachable()).count();
-
- BooleanSupplier checkReachability = () -> icmpReachabilityMap.asJavaMap()
- .values().stream().allMatch(Reachability::isReachable);
-
- timeoutSupplier(count, ICMP_REPLY_TIMEOUT_MS, checkReachability);
-
- // uninstall ICMP_REQUEST VID tagging rules
- eventExecutor.execute(() -> setAllVidTagRule(false));
-
- // uninstall ICMP_REPLY enforcing rules
- eventExecutor.execute(() -> setAllIcmpReplyRule(false));
-
- return icmpReachabilityMap.asJavaMap();
- }
-
- @Override
public Reachability probeEastWest(InstancePort srcPort, InstancePort dstPort) {
Reachability.Builder rBuilder = DefaultReachability.builder()
@@ -270,20 +214,28 @@
} else {
if (srcPort.state() == ACTIVE && dstPort.state() == ACTIVE) {
+ // if the two ports are located in different types of networks,
+ // we immediately return unreachable state
+ if (!osNetworkService.networkType(srcPort.networkId())
+ .equals(osNetworkService.networkType(dstPort.networkId()))) {
+ rBuilder.isReachable(false);
+ return rBuilder.build();
+ }
+
// install flow rules to enforce ICMP_REQUEST to be tagged and direct to ACL table
eventExecutor.execute(() -> setVidTagRule(srcPort, true));
// install flow rules to enforce forwarding ICMP_REPLY to controller
- eventExecutor.execute(() -> setIcmpReplyRule(srcPort, true));
+ eventExecutor.execute(() -> setEastWestIcmpReplyRule(srcPort, true));
timeoutPredicate(1, VID_TAG_RULE_INSTALL_TIMEOUT_MS,
this::checkVidTagRule, srcPort.ipAddress().toString());
timeoutPredicate(1, ICMP_RULE_INSTALL_TIMEOUT_MS,
- this::checkIcmpReplyRule, srcPort.ipAddress().toString());
+ this::checkEastWestIcmpReplyRule, srcPort.ipAddress().toString());
// send out ICMP ECHO request
- sendIcmpEchoRequest(srcPort, dstPort);
+ sendIcmpEchoRequest(srcPort, dstPort, null, Direction.EAST_WEST);
BooleanSupplier checkReachability = () -> icmpReachabilityMap.asJavaMap()
.values().stream().allMatch(Reachability::isReachable);
@@ -294,7 +246,7 @@
eventExecutor.execute(() -> setVidTagRule(srcPort, false));
// uninstall ICMP_REPLY enforcing rules
- eventExecutor.execute(() -> setIcmpReplyRule(srcPort, false));
+ eventExecutor.execute(() -> setEastWestIcmpReplyRule(srcPort, false));
return icmpReachabilityMap.asJavaMap()
.get(String.valueOf(icmpIdCounter.get()));
@@ -307,50 +259,48 @@
}
@Override
- public Map<String, Reachability> probeNorthSouth() {
- // TODO: require implementation
- return null;
- }
+ public Reachability probeNorthSouth(InstancePort port) {
+ Optional<OpenstackNode> gw = osNodeService.completeNodes(GATEWAY).stream().findFirst();
- @Override
- public Reachability probeNorthSouth(String netId, IpAddress ip) {
- // TODO: require implementation
- return null;
- }
-
- /**
- * Checks whether all of ICMP reply rules are added or not.
- *
- * @return true if all of ICMP reply rules are added, false otherwise
- */
- private boolean checkAllIcmpReplyRules() {
-
- Set<InstancePort> activePorts = instancePortService.instancePorts().stream()
- .filter(p -> p.state() == ACTIVE).collect(Collectors.toSet());
-
- for (InstancePort port : activePorts) {
- if (!checkIcmpReplyRule(port.ipAddress().toString())) {
- return false;
- }
+ if (!gw.isPresent()) {
+ log.warn("Gateway is not available to troubleshoot north-south traffic.");
+ return null;
}
- return true;
+ // install flow rules to enforce forwarding ICMP_REPLY to controller
+ eventExecutor.execute(() -> setNorthSouthIcmpReplyRule(port, gw.get(), true));
+
+ timeoutPredicate(1, ICMP_RULE_INSTALL_TIMEOUT_MS,
+ this::checkNorthSouthIcmpReplyRule, port.ipAddress().toString());
+
+ // send out ICMP ECHO request
+ sendIcmpEchoRequest(null, port, gw.get(), Direction.NORTH_SOUTH);
+
+ BooleanSupplier checkReachability = () -> icmpReachabilityMap.asJavaMap()
+ .values().stream().allMatch(Reachability::isReachable);
+
+ timeoutSupplier(1, ICMP_REPLY_TIMEOUT_MS, checkReachability);
+
+ // uninstall ICMP_REPLY enforcing rules
+ eventExecutor.execute(() -> setNorthSouthIcmpReplyRule(port, gw.get(), false));
+
+ return icmpReachabilityMap.asJavaMap().get(String.valueOf(icmpIdCounter.get()));
}
/**
- * Checks whether ICMP reply rule is added or not.
+ * Checks whether east-west ICMP reply rule is added or not.
*
- * @param dstIp destination IP address
+ * @param ip IP address
* @return true if ICMP reply rule is added, false otherwise
*/
- private boolean checkIcmpReplyRule(String dstIp) {
+ private boolean checkEastWestIcmpReplyRule(String ip) {
for (FlowEntry entry : flowRuleService.getFlowEntriesById(appId)) {
TrafficSelector selector = entry.selector();
- IPCriterion dstIpCriterion = (IPCriterion) selector.getCriterion(IPV4_DST);
+ IPCriterion ipCriterion = (IPCriterion) selector.getCriterion(IPV4_DST);
- if (dstIpCriterion != null &&
- dstIp.equals(dstIpCriterion.ip().address().toString()) &&
+ if (ipCriterion != null &&
+ ip.equals(ipCriterion.ip().address().toString()) &&
entry.state() == ADDED) {
return true;
}
@@ -360,21 +310,25 @@
}
/**
- * Checks whether all of ICMP request VID tagging rules are added or not.
+ * Checks whether north-south ICMP reply rule is added or not.
*
- * @return true if the rule is added, false otherwise
+ * @param ip IP address
+ * @return true if ICMP reply rule is added, false otherwise
*/
- private boolean checkAllVidTagRules() {
- Set<InstancePort> activePorts = instancePortService.instancePorts().stream()
- .filter(p -> p.state() == ACTIVE).collect(Collectors.toSet());
+ private boolean checkNorthSouthIcmpReplyRule(String ip) {
+ for (FlowEntry entry : flowRuleService.getFlowEntriesById(appId)) {
+ TrafficSelector selector = entry.selector();
- for (InstancePort port : activePorts) {
- if (!checkVidTagRule(port.ipAddress().toString())) {
- return false;
+ IPCriterion ipCriterion = (IPCriterion) selector.getCriterion(IPV4_SRC);
+
+ if (ipCriterion != null &&
+ ip.equals(ipCriterion.ip().address().toString()) &&
+ entry.state() == ADDED) {
+ return true;
}
}
- return true;
+ return false;
}
/**
@@ -400,19 +354,6 @@
}
/**
- * Installs/uninstalls all of the flow rules to match ingress fake ICMP requests.
- *
- * @param install installation flag
- */
- private void setAllVidTagRule(boolean install) {
- osNodeService.nodes(COMPUTE).forEach(n ->
- instancePortService.instancePorts().stream()
- .filter(p -> p.deviceId().equals(n.intgBridge()))
- .forEach(p -> setVidTagRule(p, install))
- );
- }
-
- /**
* Installs/uninstalls a flow rule to match ingress fake ICMP request packets,
* and tags VNI/VID, direct the tagged packet to ACL table.
*
@@ -440,25 +381,48 @@
}
/**
- * Installs/uninstalls all of the flow rules to match ICMP reply packets.
+ * Installs/uninstalls a flow rule to match north-south ICMP reply packets,
+ * direct all ICMP reply packets to the controller.
*
+ * @param port instance port
+ * @param gw gateway node
* @param install installation flag
*/
- private void setAllIcmpReplyRule(boolean install) {
- osNodeService.nodes(COMPUTE).forEach(n ->
- instancePortService.instancePorts().stream()
- .filter(p -> p.deviceId().equals(n.intgBridge()))
- .forEach(p -> setIcmpReplyRule(p, install))
- );
+ private void setNorthSouthIcmpReplyRule(InstancePort port, OpenstackNode gw,
+ boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(IpPrefix.valueOf(port.ipAddress(), PREFIX_LENGTH))
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIcmpType(ICMP.TYPE_ECHO_REPLY)
+ .matchTunnelId(getSegId(osNetworkService, port))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpSrc(instancePortService.floatingIp(port.portId()))
+ .setEthSrc(port.macAddress())
+ .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC)
+ .punt()
+ .build();
+
+ osFlowRuleService.setRule(
+ appId,
+ gw.intgBridge(),
+ selector,
+ treatment,
+ PRIORITY_ICMP_PROBE_RULE,
+ GW_COMMON_TABLE,
+ install);
}
/**
- * Installs/uninstalls a flow rule to match ICMP reply packets, direct all
- * ICMP reply packets to the controller.
+ * Installs/uninstalls a flow rule to match east-west ICMP reply packets,
+ * direct all ICMP reply packets to the controller.
*
+ * @param port instance port
* @param install installation flag
*/
- private void setIcmpReplyRule(InstancePort port, boolean install) {
+ private void setEastWestIcmpReplyRule(InstancePort port, boolean install) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(IpPrefix.valueOf(port.ipAddress(), PREFIX_LENGTH))
@@ -486,14 +450,16 @@
* @param srcPort source instance port
* @param dstPort destination instance port
*/
- private void sendIcmpEchoRequest(InstancePort srcPort, InstancePort dstPort) {
+ private void sendIcmpEchoRequest(InstancePort srcPort, InstancePort dstPort,
+ OpenstackNode gateway, Direction direction) {
short icmpSeq = INITIAL_SEQ;
short icmpId = (short) icmpIdCounter.incrementAndGet();
for (int i = 0; i < MAX_ICMP_GEN; i++) {
- packetService.emit(buildIcmpOutputPacket(srcPort, dstPort, icmpId, icmpSeq));
+ packetService.emit(buildIcmpOutputPacket(srcPort, dstPort, gateway,
+ icmpId, icmpSeq, direction));
icmpSeq++;
}
}
@@ -508,12 +474,30 @@
*/
private OutboundPacket buildIcmpOutputPacket(InstancePort srcPort,
InstancePort dstPort,
+ OpenstackNode gateway,
short icmpId,
- short icmpSeq) {
+ short icmpSeq,
+ Direction direction) {
- // TODO: need to encapsulate the frame into VXLAN/VLAN and transit the
- // packet to TABLE 0 in order to force the packet to go through all pipelines
- Ethernet ethFrame = constructIcmpPacket(srcPort, dstPort, icmpId, icmpSeq);
+ Ethernet ethFrame;
+ IpAddress srcIp;
+ IpAddress dstIp;
+ DeviceId deviceId;
+
+ if (direction == Direction.EAST_WEST) {
+ ethFrame = constructEastWestIcmpPacket(srcPort, dstPort, icmpId, icmpSeq);
+ srcIp = srcPort.ipAddress();
+ dstIp = dstPort.ipAddress();
+ deviceId = srcPort.deviceId();
+ } else if (direction == Direction.NORTH_SOUTH) {
+ ethFrame = constructNorthSouthIcmpPacket(dstPort, icmpId, icmpSeq);
+ srcIp = clusterService.getLocalNode().ip();
+ dstIp = instancePortService.floatingIp(dstPort.portId());
+ deviceId = gateway.intgBridge();
+ } else {
+ log.warn("Invalid traffic direction {}", direction);
+ return null;
+ }
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
@@ -523,8 +507,8 @@
tBuilder.setOutput(TABLE);
Reachability reachability = DefaultReachability.builder()
- .srcIp(srcPort.ipAddress())
- .dstIp(dstPort.ipAddress())
+ .srcIp(srcIp)
+ .dstIp(dstIp)
.isReachable(false)
.build();
@@ -532,7 +516,7 @@
icmpIds.add(String.valueOf(icmpId));
return new DefaultOutboundPacket(
- srcPort.deviceId(),
+ deviceId,
tBuilder.build(),
ByteBuffer.wrap(ethFrame.serialize()));
}
@@ -540,35 +524,29 @@
/**
* Constructs an ICMP packet with given source and destination IP/MAC.
*
- * @param srcPort source instance port
- * @param dstPort destination instance port
+ * @param srcIp source IP address
+ * @param dstIp destination IP address
+ * @param srcMac source MAC address
+ * @param dstMac destination MAC address
* @param icmpId ICMP identifier
* @param icmpSeq ICMP sequence number
* @return an ethernet frame which contains ICMP payload
*/
- private Ethernet constructIcmpPacket(InstancePort srcPort,
- InstancePort dstPort,
+ private Ethernet constructIcmpPacket(IpAddress srcIp, IpAddress dstIp,
+ MacAddress srcMac, MacAddress dstMac,
short icmpId, short icmpSeq) {
+
// Ethernet frame
Ethernet ethFrame = new Ethernet();
ethFrame.setEtherType(TYPE_IPV4);
- ethFrame.setSourceMACAddress(srcPort.macAddress());
-
- boolean isRemote = !srcPort.deviceId().equals(dstPort.deviceId());
-
- if (isRemote) {
- // if the source and destination VMs are located in different OVS,
- // we will assign fake gateway MAC as the destination MAC
- ethFrame.setDestinationMACAddress(DEFAULT_GATEWAY_MAC);
- } else {
- ethFrame.setDestinationMACAddress(dstPort.macAddress());
- }
+ ethFrame.setSourceMACAddress(srcMac);
+ ethFrame.setDestinationMACAddress(dstMac);
// IP packet
IPv4 iPacket = new IPv4();
- iPacket.setDestinationAddress(dstPort.ipAddress().toString());
- iPacket.setSourceAddress(srcPort.ipAddress().toString());
+ iPacket.setDestinationAddress(dstIp.toString());
+ iPacket.setSourceAddress(srcIp.toString());
iPacket.setTtl(TTL);
iPacket.setProtocol(IPv4.PROTOCOL_ICMP);
@@ -608,6 +586,56 @@
}
/**
+ * Constructs an east-west ICMP packet with given source and destination IP/MAC.
+ *
+ * @param srcPort source instance port
+ * @param dstPort destination instance port
+ * @param icmpId ICMP identifier
+ * @param icmpSeq ICMP sequence number
+ * @return an ethernet frame which contains ICMP payload
+ */
+ private Ethernet constructEastWestIcmpPacket(InstancePort srcPort,
+ InstancePort dstPort,
+ short icmpId, short icmpSeq) {
+ boolean isRemote = true;
+
+ if (srcPort.deviceId().equals(dstPort.deviceId()) &&
+ osNetworkService.gatewayIp(srcPort.portId())
+ .equals(osNetworkService.gatewayIp(dstPort.portId()))) {
+ isRemote = false;
+ }
+
+ // if the source and destination VMs are located in different OVS,
+ // we will assign fake gateway MAC as the destination MAC
+ MacAddress dstMac = isRemote ? DEFAULT_GATEWAY_MAC : dstPort.macAddress();
+
+ return constructIcmpPacket(srcPort.ipAddress(), dstPort.ipAddress(),
+ srcPort.macAddress(), dstMac, icmpId, icmpSeq);
+ }
+
+ /**
+ * Constructs a north-south ICMP packet with the given destination IP/MAC.
+ *
+ * @param dstPort destination instance port
+ * @param icmpId ICMP identifier
+ * @param icmpSeq ICMP sequence number
+ * @return an ethernet frame which contains ICMP payload
+ */
+ private Ethernet constructNorthSouthIcmpPacket(InstancePort dstPort,
+ short icmpId, short icmpSeq) {
+
+ IpAddress localIp = clusterService.getLocalNode().ip();
+ IpAddress fip = instancePortService.floatingIp(dstPort.portId());
+
+ if (fip != null) {
+ return constructIcmpPacket(localIp, fip, LOCAL_MAC, dstPort.macAddress(),
+ icmpId, icmpSeq);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Handles ICMP ECHO REPLY packets.
*
* @param ipPacket IP packet
diff --git a/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index c7fd715..fa3dabd 100644
--- a/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -21,7 +21,14 @@
<ref component-id="activeVmIpCompleter"/>
</completers>
</command>
+ <command>
+ <action class="org.onosproject.openstacktroubleshoot.cli.OpenstackNorthSouthProbeCommand"/>
+ <completers>
+ <ref component-id="activeFloatingIpCompleter"/>
+ </completers>
+ </command>
</command-bundle>
<bean id="activeVmIpCompleter" class="org.onosproject.openstacktroubleshoot.cli.ActiveVmIpCompleter"/>
+ <bean id="activeFloatingIpCompleter" class="org.onosproject.openstacktroubleshoot.cli.ActiveFloatingIpCompleter"/>
</blueprint>