python: Resolve pep8 blank line errors.
[cascardo/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 long(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()