8e23a894603f7e5cf5527d5cc14b5e423f55009b
[sliver-openvswitch.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 def ip_address(string):
28     """Verifies if string is a valid IP address"""
29     try:
30         socket.inet_aton(string)
31     except socket.error:
32         raise argparse.ArgumentTypeError("Not a valid IPv4 address")
33     return string
34
35
36 def ip_optional_mask(string):
37     """
38     Verifies if string contains a valid IP address and an optional mask in
39     CIDR notation.
40     """
41     token = string.split("/")
42     if len(token) > 2:
43         raise argparse.ArgumentTypeError("IP address and netmask must be "
44                                          "separated by a single slash")
45     elif len(token) == 2:
46         try:
47             mask = int(token[1])
48         except ValueError:
49             raise argparse.ArgumentTypeError("Netmask is not a valid integer")
50         if mask < 0 or mask > 31:
51             raise argparse.ArgumentTypeError("Netmask must be in range 0..31")
52     ip_address(token[0])
53     return string
54
55
56 def port(string):
57     """Convert a string into a TCP/UDP Port (integer)"""
58     try:
59         port_number = int(string)
60         if port_number < 1 or port_number > 65535:
61             raise argparse.ArgumentTypeError("Port is out of range")
62     except ValueError:
63         raise argparse.ArgumentTypeError("Port is not an integer")
64     return port_number
65
66
67 def ip_optional_port(string, default_port, ip_callback):
68     """Convert a string into IP and Port pair. If port was absent then use
69     default_port as the port. The third argument is a callback that verifies
70     whether IP address is given in correct format."""
71     value = string.split(':')
72     if len(value) == 1:
73         return (ip_callback(value[0]), default_port)
74     elif len(value) == 2:
75         return (ip_callback(value[0]), port(value[1]))
76     else:
77         raise argparse.ArgumentTypeError("IP address from the optional Port "
78                                          "must be colon-separated")
79
80
81 def vlan_tag(string):
82     """
83     This function verifies whether given string is a correct VLAN tag.
84     """
85     try:
86         value = int(string)
87     except ValueError:
88         raise argparse.ArgumentTypeError("VLAN tag is not a valid integer")
89     if value < 1 or value > 4094:
90         raise argparse.ArgumentTypeError("Not a valid VLAN tag. "
91                                          "VLAN tag should be in the "
92                                          "range 1..4094.")
93     return string
94
95
96 def server_endpoint(string):
97     """Converts a string OuterIP[:OuterPort],InnerIP[/Mask][:InnerPort]
98     into a 4-tuple, where:
99     1. First element is OuterIP
100     2. Second element is OuterPort (if omitted will use default value 15531)
101     3  Third element is InnerIP with optional mask
102     4. Fourth element is InnerPort (if omitted will use default value 15532)
103     """
104     value = string.split(',')
105     if len(value) == 2:
106         ret1 = ip_optional_port(value[0], CONTROL_PORT, ip_address)
107         ret2 = ip_optional_port(value[1], DATA_PORT, ip_optional_mask)
108         return (ret1[0], ret1[1], ret2[0], ret2[1])
109     else:
110         raise argparse.ArgumentTypeError("OuterIP:OuterPort and InnerIP/Mask:"
111                                          "InnerPort must be comma separated")
112
113
114 class UniqueServerAction(argparse.Action):
115     """
116     This custom action class will prevent user from entering multiple ovs-test
117     servers with the same OuterIP. If there is an server with 127.0.0.1 outer
118     IP address then it will be inserted in the front of the list.
119     """
120     def __call__(self, parser, namespace, values, option_string=None):
121         outer_ips = set()
122         endpoints = []
123         for server in values:
124             try:
125                 endpoint = server_endpoint(server)
126             except argparse.ArgumentTypeError:
127                 raise argparse.ArgumentError(self, str(sys.exc_info()[1]))
128             if endpoint[0] in outer_ips:
129                 raise argparse.ArgumentError(self, "Duplicate OuterIPs found")
130             else:
131                 outer_ips.add(endpoint[0])
132                 if endpoint[0] == "127.0.0.1":
133                     endpoints.insert(0, endpoint)
134                 else:
135                     endpoints.append(endpoint)
136         setattr(namespace, self.dest, endpoints)
137
138
139 def bandwidth(string):
140     """Convert a string (given in bits/second with optional magnitude for
141     units) into a long (bytes/second)"""
142     if re.match("^[1-9][0-9]*[MK]?$", string) is None:
143         raise argparse.ArgumentTypeError("Not a valid target bandwidth")
144     bwidth = string.replace("M", "000000")
145     bwidth = bwidth.replace("K", "000")
146     return long(bwidth) / 8  # Convert from bits to bytes
147
148
149 def tunnel_types(string):
150     """
151     This function converts a string into a list that contains all tunnel types
152     that user intended to test.
153     """
154     return string.split(',')
155
156
157 def ovs_initialize_args():
158     """
159     Initialize argument parsing for ovs-test utility.
160     """
161     parser = argparse.ArgumentParser(description='Test connectivity '
162                                                 'between two Open vSwitches.')
163
164     parser.add_argument('-v', '--version', action='version',
165                 version='ovs-test (Open vSwitch) @VERSION@')
166
167     parser.add_argument("-b", "--bandwidth", action='store',
168                 dest="targetBandwidth", default="1M", type=bandwidth,
169                 help='Target bandwidth for UDP tests in bits/second. Use '
170                 'postfix M or K to alter unit magnitude.')
171     parser.add_argument("-i", "--interval", action='store',
172                 dest="testInterval", default=5, type=int,
173                 help='Interval for how long to run each test in seconds.')
174
175     parser.add_argument("-t", "--tunnel-modes", action='store',
176                 dest="tunnelModes", default=(), type=tunnel_types,
177                 help='Do L3 tests with the given tunnel modes.')
178     parser.add_argument("-l", "--vlan-tag", action='store',
179                 dest="vlanTag", default=None, type=vlan_tag,
180                 help='Do VLAN tests and use the given VLAN tag.')
181     parser.add_argument("-d", "--direct", action='store_true',
182                 dest="direct", default=None,
183                 help='Do direct tests between both ovs-test servers.')
184
185     group = parser.add_mutually_exclusive_group(required=True)
186     group.add_argument("-s", "--server", action="store", dest="port",
187                 type=port,
188                 help='Run in server mode and wait for the client to '
189                 'connect to this port.')
190     group.add_argument('-c', "--client", nargs=2,
191                 dest="servers", action=UniqueServerAction,
192                 metavar=("SERVER1", "SERVER2"),
193                 help='Run in client mode and do tests between these '
194                 'two ovs-test servers. Each server must be specified in '
195                 'following format - OuterIP:OuterPort,InnerIP[/mask] '
196                 ':InnerPort. It is possible to start local instance of '
197                 'ovs-test server in the client mode by using 127.0.0.1 as '
198                 'OuterIP.')
199     return parser.parse_args()