mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 09:58:01 +00:00
ovs-l3ping is similar to ovs-test, but the main difference is that it does not require administrator to open firewall holes for the XML/RPC control connection. This is achieved by encapsulating the Control Connection over the L3 tunnel itself. This tool is not intended as a replacement for ovs-test, because ovs-test covers much broader set of test cases. Sample usage: Node1: ovs-l3ping -s 192.168.122.236,10.1.1.1 -t gre Node2: ovs-l3ping -c 192.168.122.220,10.1.1.2,10.1.1.1 -t gre Issue#11791 Signed-off-by: Ansis Atteka <aatteka@nicira.com>
282 lines
11 KiB
Python
282 lines
11 KiB
Python
# Copyright (c) 2011, 2012 Nicira, Inc.
|
|
#
|
|
# 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.
|
|
|
|
"""
|
|
ovsargs provide argument parsing for ovs-test utility
|
|
"""
|
|
|
|
import argparse
|
|
import re
|
|
import socket
|
|
import sys
|
|
|
|
CONTROL_PORT = 15531
|
|
DATA_PORT = 15532
|
|
|
|
def ip_address(string):
|
|
"""Verifies if string is a valid IP address"""
|
|
try:
|
|
socket.inet_aton(string)
|
|
except socket.error:
|
|
raise argparse.ArgumentTypeError("Not a valid IPv4 address")
|
|
return string
|
|
|
|
|
|
def ip_optional_mask(string):
|
|
"""
|
|
Verifies if string contains a valid IP address and an optional mask in
|
|
CIDR notation.
|
|
"""
|
|
token = string.split("/")
|
|
if len(token) > 2:
|
|
raise argparse.ArgumentTypeError("IP address and netmask must be "
|
|
"separated by a single slash")
|
|
elif len(token) == 2:
|
|
try:
|
|
mask = int(token[1])
|
|
except ValueError:
|
|
raise argparse.ArgumentTypeError("Netmask is not a valid integer")
|
|
if mask < 0 or mask > 31:
|
|
raise argparse.ArgumentTypeError("Netmask must be in range 0..31")
|
|
ip_address(token[0])
|
|
return string
|
|
|
|
|
|
def port(string):
|
|
"""Convert a string into a TCP/UDP Port (integer)"""
|
|
try:
|
|
port_number = int(string)
|
|
if port_number < 1 or port_number > 65535:
|
|
raise argparse.ArgumentTypeError("Port is out of range")
|
|
except ValueError:
|
|
raise argparse.ArgumentTypeError("Port is not an integer")
|
|
return port_number
|
|
|
|
|
|
def ip_optional_port(string, default_port, ip_callback):
|
|
"""Convert a string into IP and Port pair. If port was absent then use
|
|
default_port as the port. The third argument is a callback that verifies
|
|
whether IP address is given in correct format."""
|
|
value = string.split(':')
|
|
if len(value) == 1:
|
|
return (ip_callback(value[0]), default_port)
|
|
elif len(value) == 2:
|
|
return (ip_callback(value[0]), port(value[1]))
|
|
else:
|
|
raise argparse.ArgumentTypeError("IP address from the optional Port "
|
|
"must be colon-separated")
|
|
|
|
|
|
def ip_optional_port_port(string, default_port1, default_port2, ip_callback):
|
|
"""Convert a string into IP, Port1, Port2 tuple. If any of ports were
|
|
missing, then default ports will be used. The fourth argument is a
|
|
callback that verifies whether IP address is given in the expected
|
|
format."""
|
|
value = string.split(':')
|
|
if len(value) == 1:
|
|
return (ip_callback(value[0]), default_port1, default_port2)
|
|
elif len(value) == 2:
|
|
return (ip_callback(value[0]), port(value[1]), default_port2)
|
|
elif len(value) == 3:
|
|
return (ip_callback(value[0]), port(value[1]), port(value[2]))
|
|
else:
|
|
raise argparse.ArgumentTypeError("Expected IP address and at most "
|
|
"two colon-separated ports")
|
|
|
|
|
|
def vlan_tag(string):
|
|
"""
|
|
This function verifies whether given string is a correct VLAN tag.
|
|
"""
|
|
try:
|
|
value = int(string)
|
|
except ValueError:
|
|
raise argparse.ArgumentTypeError("VLAN tag is not a valid integer")
|
|
if value < 1 or value > 4094:
|
|
raise argparse.ArgumentTypeError("Not a valid VLAN tag. "
|
|
"VLAN tag should be in the "
|
|
"range 1..4094.")
|
|
return string
|
|
|
|
|
|
def server_endpoint(string):
|
|
"""Converts a string OuterIP[:OuterPort],InnerIP[/Mask][:InnerPort]
|
|
into a 4-tuple, where:
|
|
1. First element is OuterIP
|
|
2. Second element is OuterPort (if omitted will use default value 15531)
|
|
3 Third element is InnerIP with optional mask
|
|
4. Fourth element is InnerPort (if omitted will use default value 15532)
|
|
"""
|
|
value = string.split(',')
|
|
if len(value) == 2:
|
|
ret1 = ip_optional_port(value[0], CONTROL_PORT, ip_address)
|
|
ret2 = ip_optional_port(value[1], DATA_PORT, ip_optional_mask)
|
|
return (ret1[0], ret1[1], ret2[0], ret2[1])
|
|
else:
|
|
raise argparse.ArgumentTypeError("OuterIP:OuterPort and InnerIP/Mask:"
|
|
"InnerPort must be comma separated")
|
|
|
|
|
|
class UniqueServerAction(argparse.Action):
|
|
"""
|
|
This custom action class will prevent user from entering multiple ovs-test
|
|
servers with the same OuterIP. If there is an server with 127.0.0.1 outer
|
|
IP address then it will be inserted in the front of the list.
|
|
"""
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
outer_ips = set()
|
|
endpoints = []
|
|
for server in values:
|
|
try:
|
|
endpoint = server_endpoint(server)
|
|
except argparse.ArgumentTypeError:
|
|
raise argparse.ArgumentError(self, str(sys.exc_info()[1]))
|
|
if endpoint[0] in outer_ips:
|
|
raise argparse.ArgumentError(self, "Duplicate OuterIPs found")
|
|
else:
|
|
outer_ips.add(endpoint[0])
|
|
if endpoint[0] == "127.0.0.1":
|
|
endpoints.insert(0, endpoint)
|
|
else:
|
|
endpoints.append(endpoint)
|
|
setattr(namespace, self.dest, endpoints)
|
|
|
|
|
|
def bandwidth(string):
|
|
"""Convert a string (given in bits/second with optional magnitude for
|
|
units) into a long (bytes/second)"""
|
|
if re.match("^[1-9][0-9]*[MK]?$", string) is None:
|
|
raise argparse.ArgumentTypeError("Not a valid target bandwidth")
|
|
bwidth = string.replace("M", "000000")
|
|
bwidth = bwidth.replace("K", "000")
|
|
return long(bwidth) / 8 # Convert from bits to bytes
|
|
|
|
|
|
def tunnel_types(string):
|
|
"""
|
|
This function converts a string into a list that contains all tunnel types
|
|
that user intended to test.
|
|
"""
|
|
return string.split(',')
|
|
|
|
|
|
def l3_endpoint_client(string):
|
|
"""
|
|
This function parses command line argument string in
|
|
remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[:
|
|
ControlPort[:TestPort]] format.
|
|
"""
|
|
try:
|
|
remote_ip, me, he = string.split(',')
|
|
except ValueError:
|
|
raise argparse.ArgumentTypeError("All 3 IP addresses must be comma "
|
|
"separated.")
|
|
r = (ip_address(remote_ip),
|
|
ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask),
|
|
ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address))
|
|
return r
|
|
|
|
|
|
def l3_endpoint_server(string):
|
|
"""
|
|
This function parses a command line argument string in
|
|
remoteIP,localInnerIP[/mask][:ControlPort] format.
|
|
"""
|
|
try:
|
|
remote_ip, me = string.split(',')
|
|
except ValueError:
|
|
raise argparse.ArgumentTypeError("Both IP addresses must be comma "
|
|
"separated.")
|
|
return (ip_address(remote_ip),
|
|
ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
|
|
|
|
|
|
def ovs_initialize_args():
|
|
"""
|
|
Initialize argument parsing for ovs-test utility.
|
|
"""
|
|
parser = argparse.ArgumentParser(description='Test connectivity '
|
|
'between two Open vSwitches.')
|
|
|
|
parser.add_argument('-v', '--version', action='version',
|
|
version='ovs-test (Open vSwitch) @VERSION@')
|
|
|
|
parser.add_argument("-b", "--bandwidth", action='store',
|
|
dest="targetBandwidth", default="1M", type=bandwidth,
|
|
help='Target bandwidth for UDP tests in bits/second. Use '
|
|
'postfix M or K to alter unit magnitude.')
|
|
parser.add_argument("-i", "--interval", action='store',
|
|
dest="testInterval", default=5, type=int,
|
|
help='Interval for how long to run each test in seconds.')
|
|
|
|
parser.add_argument("-t", "--tunnel-modes", action='store',
|
|
dest="tunnelModes", default=(), type=tunnel_types,
|
|
help='Do L3 tests with the given tunnel modes.')
|
|
parser.add_argument("-l", "--vlan-tag", action='store',
|
|
dest="vlanTag", default=None, type=vlan_tag,
|
|
help='Do VLAN tests and use the given VLAN tag.')
|
|
parser.add_argument("-d", "--direct", action='store_true',
|
|
dest="direct", default=None,
|
|
help='Do direct tests between both ovs-test servers.')
|
|
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument("-s", "--server", action="store", dest="port",
|
|
type=port,
|
|
help='Run in server mode and wait for the client to '
|
|
'connect to this port.')
|
|
group.add_argument('-c', "--client", nargs=2,
|
|
dest="servers", action=UniqueServerAction,
|
|
metavar=("SERVER1", "SERVER2"),
|
|
help='Run in client mode and do tests between these '
|
|
'two ovs-test servers. Each server must be specified in '
|
|
'following format - OuterIP:OuterPort,InnerIP[/mask] '
|
|
':InnerPort. It is possible to start local instance of '
|
|
'ovs-test server in the client mode by using 127.0.0.1 as '
|
|
'OuterIP.')
|
|
return parser.parse_args()
|
|
|
|
def l3_initialize_args():
|
|
"""
|
|
Initialize argument parsing for ovs-l3ping utility.
|
|
"""
|
|
parser = argparse.ArgumentParser(description='Test L3 tunnel '
|
|
'connectivity between two Open vSwitch instances.')
|
|
|
|
parser.add_argument('-v', '--version', action='version',
|
|
version='ovs-l3ping (Open vSwitch) @VERSION@')
|
|
|
|
parser.add_argument("-b", "--bandwidth", action='store',
|
|
dest="targetBandwidth", default="1M", type=bandwidth,
|
|
help='Target bandwidth for UDP tests in bits/second. Use '
|
|
'postfix M or K to alter unit magnitude.')
|
|
parser.add_argument("-i", "--interval", action='store',
|
|
dest="testInterval", default=5, type=int,
|
|
help='Interval for how long to run each test in seconds.')
|
|
|
|
parser.add_argument("-t", "--tunnel-mode", action='store',
|
|
dest="tunnelMode", required=True,
|
|
help='Do L3 tests with this tunnel type.')
|
|
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument("-s", "--server", action="store", dest="server",
|
|
metavar="TUNNELIP,SERVER",
|
|
type=l3_endpoint_server,
|
|
help='Run in server mode and wait for the client to '
|
|
'connect.')
|
|
group.add_argument('-c', "--client", action="store", dest="client",
|
|
metavar="TUNNELIP,CLIENT,SERVER",
|
|
type=l3_endpoint_client,
|
|
help='Run in client mode and connect to the server.')
|
|
return parser.parse_args()
|