]> git.proxmox.com Git - ovs.git/blame - utilities/ovs-vlan-test.in
classifier: Make use of the classifier thread safe.
[ovs.git] / utilities / ovs-vlan-test.in
CommitLineData
01ca1bfc
EJ
1#! @PYTHON@
2#
e0edde6f 3# Copyright (c) 2010 Nicira, Inc.
01ca1bfc
EJ
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at:
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import BaseHTTPServer
18import getopt
19import httplib
20import os
21import threading
22import time
23import signal #Causes keyboard interrupts to go to the main thread.
24import socket
25import sys
26
27print_safe_lock = threading.Lock()
28def print_safe(s):
29 print_safe_lock.acquire()
30 print(s)
31 print_safe_lock.release()
32
33def start_thread(target, args):
34 t = threading.Thread(target=target, args=args)
35 t.setDaemon(True)
36 t.start()
37 return t
38
39#Caller is responsible for catching socket.error exceptions.
40def send_packet(key, length, dest_ip, dest_port):
41
42 length -= 20 + 8 #IP and UDP headers.
43
44 packet = str(key)
45 packet += chr(0) * (length - len(packet))
46
47 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
48 sock.sendto(packet, (dest_ip, dest_port))
49 sock.close()
50\f
51#UDP Receiver
52class UDPReceiver:
53 def __init__(self, vlan_ip, vlan_port):
54 self.vlan_ip = vlan_ip
55 self.vlan_port = vlan_port
56 self.recv_callbacks = {}
57 self.udp_run = False
58
59 def recv_packet(self, key, success_callback, timeout_callback):
60
61 event = threading.Event()
62
63 def timeout_cb():
64 timeout_callback()
65 event.set()
66
67 timer = threading.Timer(30, timeout_cb)
68 timer.daemon = True
69
70 def success_cb():
71 timer.cancel()
72 success_callback()
73 event.set()
74
75 # Start the timer first to avoid a timer.cancel() race condition.
76 timer.start()
77 self.recv_callbacks[key] = success_cb
78 return event
79
80 def udp_receiver(self):
81
82 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
83 sock.settimeout(1)
84
85 try:
86 sock.bind((self.vlan_ip, self.vlan_port))
87 except socket.error, e:
88 print_safe('Failed to bind to %s:%d with error: %s'
89 % (self.vlan_ip, self.vlan_port, e))
90 os._exit(1) #sys.exit only exits the current thread.
91
92 while self.udp_run:
93
94 try:
95 data, _ = sock.recvfrom(4096)
96 except socket.timeout:
97 continue
98 except socket.error, e:
99 print_safe('Failed to receive from %s:%d with error: %s'
100 % (self.vlan_ip, self.vlan_port, e))
101 os._exit(1)
102
103 data_str = data.split(chr(0))[0]
104
105 if not data_str.isdigit():
106 continue
107
108 key = int(data_str)
109
110 if key in self.recv_callbacks:
111 self.recv_callbacks[key]()
112 del self.recv_callbacks[key]
113
114 def start(self):
115 self.udp_run = True
116 start_thread(self.udp_receiver, ())
117
118 def stop(self):
119 self.udp_run = False
120\f
121#Server
122vlan_server = None
123class VlanServer:
124
125 def __init__(self, server_ip, server_port, vlan_ip, vlan_port):
126 global vlan_server
127
128 vlan_server = self
129
130 self.server_ip = server_ip
131 self.server_port = server_port
132
133 self.recv_response = '%s:%d:' % (vlan_ip, vlan_port)
134
135 self.result = {}
136 self.result_lock = threading.Lock()
137
138 self._test_id = 0
139 self._test_id_lock = threading.Lock()
140
141 self.udp_recv = UDPReceiver(vlan_ip, vlan_port)
142
143 def get_test_id(self):
144 self._test_id_lock.acquire()
145
146 self._test_id += 1
147 ret = self._test_id
148
149 self._test_id_lock.release()
150 return ret
151
152 def set_result(self, key, value):
153
154 self.result_lock.acquire()
155
156 if key not in self.result:
157 self.result[key] = value
158
159 self.result_lock.release()
160
161 def recv(self, test_id):
162 self.udp_recv.recv_packet(test_id,
163 lambda : self.set_result(test_id, 'Success'),
164 lambda : self.set_result(test_id, 'Timeout'))
165
166 return self.recv_response + str(test_id)
167
168 def send(self, test_id, data):
169 try:
170 ip, port, size = data.split(':')
171 port = int(port)
172 size = int(size)
173 except ValueError:
174 self.set_result(test_id,
175 'Server failed to parse send request: %s' % data)
176 return
177
178 def send_thread():
179 send_time = 10
180 for _ in range(send_time * 2):
181 try:
182 send_packet(test_id, size, ip, port)
183 except socket.error, e:
184 self.set_result(test_id, 'Failure: ' + str(e))
185 return
186 time.sleep(.5)
187
188 self.set_result(test_id, 'Success')
189
190 start_thread(send_thread, ())
191
192 return str(test_id)
193
194 def run(self):
195 self.udp_recv.start()
196 try:
197 BaseHTTPServer.HTTPServer((self.server_ip, self.server_port),
198 VlanServerHandler).serve_forever()
199 except socket.error, e:
200 print_safe('Failed to start control server: %s' % e)
201 self.udp_recv.stop()
202
203 return 1
204
205class VlanServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
206 def do_GET(self):
207
208 #Guarantee three arguments.
209 path = (self.path.lower().lstrip('/') + '//').split('/')
210
211 resp = 404
212 body = None
213
214 if path[0] == 'start':
215 test_id = vlan_server.get_test_id()
216
217 if path[1] == 'recv':
218 resp = 200
219 body = vlan_server.recv(test_id)
220 elif path[1] == 'send':
221 resp = 200
222 body = vlan_server.send(test_id, path[2])
223 elif (path[0] == 'result'
224 and path[1].isdigit()
225 and int(path[1]) in vlan_server.result):
226 resp = 200
227 body = vlan_server.result[int(path[1])]
228 elif path[0] == 'ping':
229 resp = 200
230 body = 'pong'
231
232 self.send_response(resp)
233 self.end_headers()
234
235 if body:
236 self.wfile.write(body)
237\f
238#Client
239class VlanClient:
240
241 def __init__(self, server_ip, server_port, vlan_ip, vlan_port):
242 self.server_ip_port = '%s:%d' % (server_ip, server_port)
243 self.vlan_ip_port = "%s:%d" % (vlan_ip, vlan_port)
244 self.udp_recv = UDPReceiver(vlan_ip, vlan_port)
245
246 def request(self, resource):
247 conn = httplib.HTTPConnection(self.server_ip_port)
248 conn.request('GET', resource)
249 return conn
250
251 def send(self, size):
252
253 def error_msg(e):
254 print_safe('Send size %d unsuccessful: %s' % (size, e))
255
256 try:
257 conn = self.request('/start/recv')
258 data = conn.getresponse().read()
259 except (socket.error, httplib.HTTPException), e:
260 error_msg(e)
261 return False
262
263 try:
264 ip, port, test_id = data.split(':')
265 port = int(port)
266 test_id = int(test_id)
267 except ValueError:
268 error_msg("Received invalid response from control server (%s)" %
269 data)
270 return False
271
272 send_time = 5
273
274 for _ in range(send_time * 4):
275
276 try:
277 send_packet(test_id, size, ip, port)
278 resp = self.request('/result/%d' % test_id).getresponse()
279 data = resp.read()
280 except (socket.error, httplib.HTTPException), e:
281 error_msg(e)
282 return False
283
284 if resp.status == 200 and data == 'Success':
285 print_safe('Send size %d successful' % size)
286 return True
287 elif resp.status == 200:
288 error_msg(data)
289 return False
290
291 time.sleep(.25)
292
293 error_msg('Timeout')
294 return False
295
296 def recv(self, size):
297
298 def error_msg(e):
299 print_safe('Receive size %d unsuccessful: %s' % (size, e))
300
301 resource = '/start/send/%s:%d' % (self.vlan_ip_port, size)
302 try:
303 conn = self.request(resource)
304 test_id = conn.getresponse().read()
305 except (socket.error, httplib.HTTPException), e:
306 error_msg(e)
307 return False
308
309 if not test_id.isdigit():
310 error_msg('Invalid response %s' % test_id)
311 return False
312
313 success = [False] #Primitive datatypes can't be set from closures.
314
315 def success_cb():
316 success[0] = True
317
318 def failure_cb():
319 success[0] = False
320
321 self.udp_recv.recv_packet(int(test_id), success_cb, failure_cb).wait()
322
323 if success[0]:
324 print_safe('Receive size %d successful' % size)
325 else:
326 error_msg('Timeout')
327
328 return success[0]
329
330 def server_up(self):
331
332 def error_msg(e):
333 print_safe('Failed control server connectivity test: %s' % e)
334
335 try:
336 resp = self.request('/ping').getresponse()
337 data = resp.read()
338 except (socket.error, httplib.HTTPException), e:
339 error_msg(e)
340 return False
341
342 if resp.status != 200:
343 error_msg('Invalid status %d' % resp.status)
344 elif data != 'pong':
345 error_msg('Invalid response %s' % data)
346
347 return True
348
349 def run(self):
350
351 if not self.server_up():
352 return 1
353
354 self.udp_recv.start()
355
356 success = True
357 for size in [50, 500, 1000, 1500]:
358 success = self.send(size) and success
359 success = self.recv(size) and success
360
361 self.udp_recv.stop()
362
363 if success:
364 print_safe('OK')
365 return 0
0acae9fd
EJ
366 else:
367 print_safe('FAILED')
368 return 1
01ca1bfc
EJ
369\f
370def usage():
371 print_safe("""\
372%(argv0)s: Test vlan connectivity
373usage: %(argv0)s server vlan
374
375The following options are also available:
376 -s, --server run in server mode
377 -h, --help display this help message
378 -V, --version display version information\
379""" % {'argv0': sys.argv[0]})
380
381def main():
382
383 try:
384 options, args = getopt.gnu_getopt(sys.argv[1:], 'hVs',
385 ['help', 'version', 'server'])
386 except getopt.GetoptError, geo:
387 print_safe('%s: %s\n' % (sys.argv[0], geo.msg))
388 return 1
389
390 server = False
391 for key, _ in options:
392 if key in ['-h', '--help']:
393 usage()
394 return 0
395 elif key in ['-V', '--version']:
396 print_safe('ovs-vlan-test (Open vSwitch) @VERSION@')
397 return 0
398 elif key in ['-s', '--server']:
399 server = True
400 else:
401 print_safe('Unexpected option %s. (use --help for help)' % key)
402 return 1
403
404 if len(args) != 2:
405 print_safe('Expecting two arguments. (use --help for help)')
406 return 1
407
408 try:
409 server_ip, server_port = args[0].split(':')
410 server_port = int(server_port)
411 except ValueError:
412 server_ip = args[0]
413 server_port = 80
414
415 try:
416 vlan_ip, vlan_port = args[1].split(':')
417 vlan_port = int(vlan_port)
418 except ValueError:
419 vlan_ip = args[1]
420 vlan_port = 15213
421
422 if server:
423 return VlanServer(server_ip, server_port, vlan_ip, vlan_port).run()
424 else:
425 return VlanClient(server_ip, server_port, vlan_ip, vlan_port).run()
426
427if __name__ == '__main__':
428 main_ret = main()
429
430 # Python can throw exceptions if threads are running at exit.
431 for th in threading.enumerate():
432 if th != threading.currentThread():
433 th.join()
434
435 sys.exit(main_ret)