]> git.proxmox.com Git - mirror_ovs.git/blob - python/ovstest/args.py
cirrus: Use FreeBSD 12.2.
[mirror_ovs.git] / python / ovstest / args.py
1 # Copyright (c) 2011, 2012 Nicira, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """
16 ovsargs provide argument parsing for ovs-test utility
17 """
18
19 import argparse
20 import re
21 import socket
22 import sys
23
24 CONTROL_PORT = 15531
25 DATA_PORT = 15532
26
27
28 def ip_address(string):
29 """Verifies if string is a valid IP address"""
30 try:
31 socket.inet_aton(string)
32 except socket.error:
33 raise argparse.ArgumentTypeError("Not a valid IPv4 address")
34 return string
35
36
37 def ip_optional_mask(string):
38 """
39 Verifies if string contains a valid IP address and an optional mask in
40 CIDR notation.
41 """
42 token = string.split("/")
43 if len(token) > 2:
44 raise argparse.ArgumentTypeError("IP address and netmask must be "
45 "separated by a single slash")
46 elif len(token) == 2:
47 try:
48 mask = int(token[1])
49 except ValueError:
50 raise argparse.ArgumentTypeError("Netmask is not a valid integer")
51 if mask < 0 or mask > 31:
52 raise argparse.ArgumentTypeError("Netmask must be in range 0..31")
53 ip_address(token[0])
54 return string
55
56
57 def port(string):
58 """Convert a string into a TCP/UDP Port (integer)"""
59 try:
60 port_number = int(string)
61 if port_number < 1 or port_number > 65535:
62 raise argparse.ArgumentTypeError("Port is out of range")
63 except ValueError:
64 raise argparse.ArgumentTypeError("Port is not an integer")
65 return port_number
66
67
68 def ip_optional_port(string, default_port, ip_callback):
69 """Convert a string into IP and Port pair. If port was absent then use
70 default_port as the port. The third argument is a callback that verifies
71 whether IP address is given in correct format."""
72 value = string.split(':')
73 if len(value) == 1:
74 return (ip_callback(value[0]), default_port)
75 elif len(value) == 2:
76 return (ip_callback(value[0]), port(value[1]))
77 else:
78 raise argparse.ArgumentTypeError("IP address from the optional Port "
79 "must be colon-separated")
80
81
82 def ip_optional_port_port(string, default_port1, default_port2, ip_callback):
83 """Convert a string into IP, Port1, Port2 tuple. If any of ports were
84 missing, then default ports will be used. The fourth argument is a
85 callback that verifies whether IP address is given in the expected
86 format."""
87 value = string.split(':')
88 if len(value) == 1:
89 return (ip_callback(value[0]), default_port1, default_port2)
90 elif len(value) == 2:
91 return (ip_callback(value[0]), port(value[1]), default_port2)
92 elif len(value) == 3:
93 return (ip_callback(value[0]), port(value[1]), port(value[2]))
94 else:
95 raise argparse.ArgumentTypeError("Expected IP address and at most "
96 "two colon-separated ports")
97
98
99 def vlan_tag(string):
100 """
101 This function verifies whether given string is a correct VLAN tag.
102 """
103 try:
104 value = int(string)
105 except ValueError:
106 raise argparse.ArgumentTypeError("VLAN tag is not a valid integer")
107 if value < 1 or value > 4094:
108 raise argparse.ArgumentTypeError("Not a valid VLAN tag. "
109 "VLAN tag should be in the "
110 "range 1..4094.")
111 return string
112
113
114 def server_endpoint(string):
115 """Converts a string OuterIP[:OuterPort],InnerIP[/Mask][:InnerPort]
116 into a 4-tuple, where:
117 1. First element is OuterIP
118 2. Second element is OuterPort (if omitted will use default value 15531)
119 3 Third element is InnerIP with optional mask
120 4. Fourth element is InnerPort (if omitted will use default value 15532)
121 """
122 value = string.split(',')
123 if len(value) == 2:
124 ret1 = ip_optional_port(value[0], CONTROL_PORT, ip_address)
125 ret2 = ip_optional_port(value[1], DATA_PORT, ip_optional_mask)
126 return (ret1[0], ret1[1], ret2[0], ret2[1])
127 else:
128 raise argparse.ArgumentTypeError("OuterIP:OuterPort and InnerIP/Mask:"
129 "InnerPort must be comma separated")
130
131
132 class UniqueServerAction(argparse.Action):
133 """
134 This custom action class will prevent user from entering multiple ovs-test
135 servers with the same OuterIP. If there is an server with 127.0.0.1 outer
136 IP address then it will be inserted in the front of the list.
137 """
138 def __call__(self, parser, namespace, values, option_string=None):
139 outer_ips = set()
140 endpoints = []
141 for server in values:
142 try:
143 endpoint = server_endpoint(server)
144 except argparse.ArgumentTypeError:
145 raise argparse.ArgumentError(self, str(sys.exc_info()[1]))
146 if endpoint[0] in outer_ips:
147 raise argparse.ArgumentError(self, "Duplicate OuterIPs found")
148 else:
149 outer_ips.add(endpoint[0])
150 if endpoint[0] == "127.0.0.1":
151 endpoints.insert(0, endpoint)
152 else:
153 endpoints.append(endpoint)
154 setattr(namespace, self.dest, endpoints)
155
156
157 def bandwidth(string):
158 """Convert a string (given in bits/second with optional magnitude for
159 units) into a long (bytes/second)"""
160 if re.match("^[1-9][0-9]*[MK]?$", string) is None:
161 raise argparse.ArgumentTypeError("Not a valid target bandwidth")
162 bwidth = string.replace("M", "000000")
163 bwidth = bwidth.replace("K", "000")
164 return int(bwidth) / 8 # Convert from bits to bytes
165
166
167 def tunnel_types(string):
168 """
169 This function converts a string into a list that contains all tunnel types
170 that user intended to test.
171 """
172 return string.split(',')
173
174
175 def l3_endpoint_client(string):
176 """
177 This function parses command line argument string in
178 remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[:
179 ControlPort[:TestPort]] format.
180 """
181 try:
182 remote_ip, me, he = string.split(',')
183 except ValueError:
184 raise argparse.ArgumentTypeError("All 3 IP addresses must be comma "
185 "separated.")
186 r = (ip_address(remote_ip),
187 ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask),
188 ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address))
189 return r
190
191
192 def l3_endpoint_server(string):
193 """
194 This function parses a command line argument string in
195 remoteIP,localInnerIP[/mask][:ControlPort] format.
196 """
197 try:
198 remote_ip, me = string.split(',')
199 except ValueError:
200 raise argparse.ArgumentTypeError("Both IP addresses must be comma "
201 "separated.")
202 return (ip_address(remote_ip),
203 ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
204
205
206 def ovs_initialize_args():
207 """
208 Initialize argument parsing for ovs-test utility.
209 """
210 parser = argparse.ArgumentParser(description='Test connectivity '
211 'between two Open vSwitches.')
212
213 parser.add_argument('-v', '--version', action='version',
214 version='ovs-test (Open vSwitch) @VERSION@')
215
216 parser.add_argument("-b", "--bandwidth", action='store',
217 dest="targetBandwidth", default="1M", type=bandwidth,
218 help='Target bandwidth for UDP tests in bits/second. Use '
219 'postfix M or K to alter unit magnitude.')
220 parser.add_argument("-i", "--interval", action='store',
221 dest="testInterval", default=5, type=int,
222 help='Interval for how long to run each test in seconds.')
223
224 parser.add_argument("-t", "--tunnel-modes", action='store',
225 dest="tunnelModes", default=(), type=tunnel_types,
226 help='Do L3 tests with the given tunnel modes.')
227 parser.add_argument("-l", "--vlan-tag", action='store',
228 dest="vlanTag", default=None, type=vlan_tag,
229 help='Do VLAN tests and use the given VLAN tag.')
230 parser.add_argument("-d", "--direct", action='store_true',
231 dest="direct", default=None,
232 help='Do direct tests between both ovs-test servers.')
233
234 group = parser.add_mutually_exclusive_group(required=True)
235 group.add_argument("-s", "--server", action="store", dest="port",
236 type=port,
237 help='Run in server mode and wait for the client to '
238 'connect to this port.')
239 group.add_argument('-c', "--client", nargs=2,
240 dest="servers", action=UniqueServerAction,
241 metavar=("SERVER1", "SERVER2"),
242 help='Run in client mode and do tests between these '
243 'two ovs-test servers. Each server must be specified in '
244 'following format - OuterIP:OuterPort,InnerIP[/mask] '
245 ':InnerPort. It is possible to start local instance of '
246 'ovs-test server in the client mode by using 127.0.0.1 as '
247 'OuterIP.')
248 return parser.parse_args()
249
250
251 def l3_initialize_args():
252 """
253 Initialize argument parsing for ovs-l3ping utility.
254 """
255 parser = argparse.ArgumentParser(description='Test L3 tunnel '
256 'connectivity between two Open vSwitch instances.')
257
258 parser.add_argument('-v', '--version', action='version',
259 version='ovs-l3ping (Open vSwitch) @VERSION@')
260
261 parser.add_argument("-b", "--bandwidth", action='store',
262 dest="targetBandwidth", default="1M", type=bandwidth,
263 help='Target bandwidth for UDP tests in bits/second. Use '
264 'postfix M or K to alter unit magnitude.')
265 parser.add_argument("-i", "--interval", action='store',
266 dest="testInterval", default=5, type=int,
267 help='Interval for how long to run each test in seconds.')
268
269 parser.add_argument("-t", "--tunnel-mode", action='store',
270 dest="tunnelMode", required=True,
271 help='Do L3 tests with this tunnel type.')
272
273 group = parser.add_mutually_exclusive_group(required=True)
274 group.add_argument("-s", "--server", action="store", dest="server",
275 metavar="TUNNELIP,SERVER",
276 type=l3_endpoint_server,
277 help='Run in server mode and wait for the client to '
278 'connect.')
279 group.add_argument('-c', "--client", action="store", dest="client",
280 metavar="TUNNELIP,CLIENT,SERVER",
281 type=l3_endpoint_client,
282 help='Run in client mode and connect to the server.')
283 return parser.parse_args()