mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
Since on CentOS/RHEL the builds are based on stable branches and not on tags for debugging purpose it's better to have the downstream version as version so it's easier to know which commits are included in a build. This commit adds --with-version-suffix as ./configure option in order to set an OVS version suffix that should be shown to the user via ovs-vsctl -V and, so, also on database, on ovs-vsctl show and the other utilities. --with-version-suffix is used in Fedora/CentOS/RHEL spec file in order to have the version be aligned with the downstream one. Signed-off-by: Timothy Redaelli <tredaelli@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
436 lines
12 KiB
Plaintext
Executable File
436 lines
12 KiB
Plaintext
Executable File
#! @PYTHON3@
|
||
#
|
||
# Copyright (c) 2010 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.
|
||
|
||
import getopt
|
||
import http.client
|
||
import http.server
|
||
import os
|
||
import threading
|
||
import time
|
||
import signal #Causes keyboard interrupts to go to the main thread.
|
||
import socket
|
||
import sys
|
||
|
||
print_safe_lock = threading.Lock()
|
||
def print_safe(s):
|
||
print_safe_lock.acquire()
|
||
print(s)
|
||
print_safe_lock.release()
|
||
|
||
def start_thread(target, args):
|
||
t = threading.Thread(target=target, args=args)
|
||
t.setDaemon(True)
|
||
t.start()
|
||
return t
|
||
|
||
#Caller is responsible for catching socket.error exceptions.
|
||
def send_packet(key, length, dest_ip, dest_port):
|
||
|
||
length -= 20 + 8 #IP and UDP headers.
|
||
|
||
packet = str(key)
|
||
packet += chr(0) * (length - len(packet))
|
||
|
||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
sock.sendto(packet, (dest_ip, dest_port))
|
||
sock.close()
|
||
|
||
#UDP Receiver
|
||
class UDPReceiver:
|
||
def __init__(self, vlan_ip, vlan_port):
|
||
self.vlan_ip = vlan_ip
|
||
self.vlan_port = vlan_port
|
||
self.recv_callbacks = {}
|
||
self.udp_run = False
|
||
|
||
def recv_packet(self, key, success_callback, timeout_callback):
|
||
|
||
event = threading.Event()
|
||
|
||
def timeout_cb():
|
||
timeout_callback()
|
||
event.set()
|
||
|
||
timer = threading.Timer(30, timeout_cb)
|
||
timer.daemon = True
|
||
|
||
def success_cb():
|
||
timer.cancel()
|
||
success_callback()
|
||
event.set()
|
||
|
||
# Start the timer first to avoid a timer.cancel() race condition.
|
||
timer.start()
|
||
self.recv_callbacks[key] = success_cb
|
||
return event
|
||
|
||
def udp_receiver(self):
|
||
|
||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
sock.settimeout(1)
|
||
|
||
try:
|
||
sock.bind((self.vlan_ip, self.vlan_port))
|
||
except socket.error as e:
|
||
print_safe('Failed to bind to %s:%d with error: %s'
|
||
% (self.vlan_ip, self.vlan_port, e))
|
||
os._exit(1) #sys.exit only exits the current thread.
|
||
|
||
while self.udp_run:
|
||
|
||
try:
|
||
data, _ = sock.recvfrom(4096)
|
||
except socket.timeout:
|
||
continue
|
||
except socket.error as e:
|
||
print_safe('Failed to receive from %s:%d with error: %s'
|
||
% (self.vlan_ip, self.vlan_port, e))
|
||
os._exit(1)
|
||
|
||
data_str = data.split(chr(0))[0]
|
||
|
||
if not data_str.isdigit():
|
||
continue
|
||
|
||
key = int(data_str)
|
||
|
||
if key in self.recv_callbacks:
|
||
self.recv_callbacks[key]()
|
||
del self.recv_callbacks[key]
|
||
|
||
def start(self):
|
||
self.udp_run = True
|
||
start_thread(self.udp_receiver, ())
|
||
|
||
def stop(self):
|
||
self.udp_run = False
|
||
|
||
#Server
|
||
vlan_server = None
|
||
class VlanServer:
|
||
|
||
def __init__(self, server_ip, server_port, vlan_ip, vlan_port):
|
||
global vlan_server
|
||
|
||
vlan_server = self
|
||
|
||
self.server_ip = server_ip
|
||
self.server_port = server_port
|
||
|
||
self.recv_response = '%s:%d:' % (vlan_ip, vlan_port)
|
||
|
||
self.result = {}
|
||
self.result_lock = threading.Lock()
|
||
|
||
self._test_id = 0
|
||
self._test_id_lock = threading.Lock()
|
||
|
||
self.udp_recv = UDPReceiver(vlan_ip, vlan_port)
|
||
|
||
def get_test_id(self):
|
||
self._test_id_lock.acquire()
|
||
|
||
self._test_id += 1
|
||
ret = self._test_id
|
||
|
||
self._test_id_lock.release()
|
||
return ret
|
||
|
||
def set_result(self, key, value):
|
||
|
||
self.result_lock.acquire()
|
||
|
||
if key not in self.result:
|
||
self.result[key] = value
|
||
|
||
self.result_lock.release()
|
||
|
||
def recv(self, test_id):
|
||
self.udp_recv.recv_packet(test_id,
|
||
lambda : self.set_result(test_id, 'Success'),
|
||
lambda : self.set_result(test_id, 'Timeout'))
|
||
|
||
return self.recv_response + str(test_id)
|
||
|
||
def send(self, test_id, data):
|
||
try:
|
||
ip, port, size = data.split(':')
|
||
port = int(port)
|
||
size = int(size)
|
||
except ValueError:
|
||
self.set_result(test_id,
|
||
'Server failed to parse send request: %s' % data)
|
||
return
|
||
|
||
def send_thread():
|
||
send_time = 10
|
||
for _ in range(send_time * 2):
|
||
try:
|
||
send_packet(test_id, size, ip, port)
|
||
except socket.error as e:
|
||
self.set_result(test_id, 'Failure: ' + str(e))
|
||
return
|
||
time.sleep(.5)
|
||
|
||
self.set_result(test_id, 'Success')
|
||
|
||
start_thread(send_thread, ())
|
||
|
||
return str(test_id)
|
||
|
||
def run(self):
|
||
self.udp_recv.start()
|
||
try:
|
||
http.server.HTTPServer((self.server_ip, self.server_port),
|
||
VlanServerHandler).serve_forever()
|
||
except socket.error as e:
|
||
print_safe('Failed to start control server: %s' % e)
|
||
self.udp_recv.stop()
|
||
|
||
return 1
|
||
|
||
class VlanServerHandler(http.server.BaseHTTPRequestHandler):
|
||
def do_GET(self):
|
||
|
||
#Guarantee three arguments.
|
||
path = (self.path.lower().lstrip('/') + '//').split('/')
|
||
|
||
resp = 404
|
||
body = None
|
||
|
||
if path[0] == 'start':
|
||
test_id = vlan_server.get_test_id()
|
||
|
||
if path[1] == 'recv':
|
||
resp = 200
|
||
body = vlan_server.recv(test_id)
|
||
elif path[1] == 'send':
|
||
resp = 200
|
||
body = vlan_server.send(test_id, path[2])
|
||
elif (path[0] == 'result'
|
||
and path[1].isdigit()
|
||
and int(path[1]) in vlan_server.result):
|
||
resp = 200
|
||
body = vlan_server.result[int(path[1])]
|
||
elif path[0] == 'ping':
|
||
resp = 200
|
||
body = 'pong'
|
||
|
||
self.send_response(resp)
|
||
self.end_headers()
|
||
|
||
if body:
|
||
self.wfile.write(body)
|
||
|
||
#Client
|
||
class VlanClient:
|
||
|
||
def __init__(self, server_ip, server_port, vlan_ip, vlan_port):
|
||
self.server_ip_port = '%s:%d' % (server_ip, server_port)
|
||
self.vlan_ip_port = "%s:%d" % (vlan_ip, vlan_port)
|
||
self.udp_recv = UDPReceiver(vlan_ip, vlan_port)
|
||
|
||
def request(self, resource):
|
||
conn = http.client.HTTPConnection(self.server_ip_port)
|
||
conn.request('GET', resource)
|
||
return conn
|
||
|
||
def send(self, size):
|
||
|
||
def error_msg(e):
|
||
print_safe('Send size %d unsuccessful: %s' % (size, e))
|
||
|
||
try:
|
||
conn = self.request('/start/recv')
|
||
data = conn.getresponse().read()
|
||
except (socket.error, http.client.HTTPException) as e:
|
||
error_msg(e)
|
||
return False
|
||
|
||
try:
|
||
ip, port, test_id = data.split(':')
|
||
port = int(port)
|
||
test_id = int(test_id)
|
||
except ValueError:
|
||
error_msg("Received invalid response from control server (%s)" %
|
||
data)
|
||
return False
|
||
|
||
send_time = 5
|
||
|
||
for _ in range(send_time * 4):
|
||
|
||
try:
|
||
send_packet(test_id, size, ip, port)
|
||
resp = self.request('/result/%d' % test_id).getresponse()
|
||
data = resp.read()
|
||
except (socket.error, http.client.HTTPException) as e:
|
||
error_msg(e)
|
||
return False
|
||
|
||
if resp.status == 200 and data == 'Success':
|
||
print_safe('Send size %d successful' % size)
|
||
return True
|
||
elif resp.status == 200:
|
||
error_msg(data)
|
||
return False
|
||
|
||
time.sleep(.25)
|
||
|
||
error_msg('Timeout')
|
||
return False
|
||
|
||
def recv(self, size):
|
||
|
||
def error_msg(e):
|
||
print_safe('Receive size %d unsuccessful: %s' % (size, e))
|
||
|
||
resource = '/start/send/%s:%d' % (self.vlan_ip_port, size)
|
||
try:
|
||
conn = self.request(resource)
|
||
test_id = conn.getresponse().read()
|
||
except (socket.error, http.client.HTTPException) as e:
|
||
error_msg(e)
|
||
return False
|
||
|
||
if not test_id.isdigit():
|
||
error_msg('Invalid response %s' % test_id)
|
||
return False
|
||
|
||
success = [False] #Primitive datatypes can't be set from closures.
|
||
|
||
def success_cb():
|
||
success[0] = True
|
||
|
||
def failure_cb():
|
||
success[0] = False
|
||
|
||
self.udp_recv.recv_packet(int(test_id), success_cb, failure_cb).wait()
|
||
|
||
if success[0]:
|
||
print_safe('Receive size %d successful' % size)
|
||
else:
|
||
error_msg('Timeout')
|
||
|
||
return success[0]
|
||
|
||
def server_up(self):
|
||
|
||
def error_msg(e):
|
||
print_safe('Failed control server connectivity test: %s' % e)
|
||
|
||
try:
|
||
resp = self.request('/ping').getresponse()
|
||
data = resp.read()
|
||
except (socket.error, http.client.HTTPException) as e:
|
||
error_msg(e)
|
||
return False
|
||
|
||
if resp.status != 200:
|
||
error_msg('Invalid status %d' % resp.status)
|
||
elif data != 'pong':
|
||
error_msg('Invalid response %s' % data)
|
||
|
||
return True
|
||
|
||
def run(self):
|
||
|
||
if not self.server_up():
|
||
return 1
|
||
|
||
self.udp_recv.start()
|
||
|
||
success = True
|
||
for size in [50, 500, 1000, 1500]:
|
||
success = self.send(size) and success
|
||
success = self.recv(size) and success
|
||
|
||
self.udp_recv.stop()
|
||
|
||
if success:
|
||
print_safe('OK')
|
||
return 0
|
||
else:
|
||
print_safe('FAILED')
|
||
return 1
|
||
|
||
def usage():
|
||
print_safe("""\
|
||
%(argv0)s: Test vlan connectivity
|
||
usage: %(argv0)s server vlan
|
||
|
||
The following options are also available:
|
||
-s, --server run in server mode
|
||
-h, --help display this help message
|
||
-V, --version display version information\
|
||
""" % {'argv0': sys.argv[0]})
|
||
|
||
def main():
|
||
|
||
try:
|
||
options, args = getopt.gnu_getopt(sys.argv[1:], 'hVs',
|
||
['help', 'version', 'server'])
|
||
except getopt.GetoptError as geo:
|
||
print_safe('%s: %s\n' % (sys.argv[0], geo.msg))
|
||
return 1
|
||
|
||
server = False
|
||
for key, _ in options:
|
||
if key in ['-h', '--help']:
|
||
usage()
|
||
return 0
|
||
elif key in ['-V', '--version']:
|
||
print_safe('ovs-vlan-test (Open vSwitch) @VERSION@@VERSION_SUFFIX@')
|
||
return 0
|
||
elif key in ['-s', '--server']:
|
||
server = True
|
||
else:
|
||
print_safe('Unexpected option %s. (use --help for help)' % key)
|
||
return 1
|
||
|
||
if len(args) != 2:
|
||
print_safe('Expecting two arguments. (use --help for help)')
|
||
return 1
|
||
|
||
try:
|
||
server_ip, server_port = args[0].split(':')
|
||
server_port = int(server_port)
|
||
except ValueError:
|
||
server_ip = args[0]
|
||
server_port = 80
|
||
|
||
try:
|
||
vlan_ip, vlan_port = args[1].split(':')
|
||
vlan_port = int(vlan_port)
|
||
except ValueError:
|
||
vlan_ip = args[1]
|
||
vlan_port = 15213
|
||
|
||
if server:
|
||
return VlanServer(server_ip, server_port, vlan_ip, vlan_port).run()
|
||
else:
|
||
return VlanClient(server_ip, server_port, vlan_ip, vlan_port).run()
|
||
|
||
if __name__ == '__main__':
|
||
main_ret = main()
|
||
|
||
# Python can throw exceptions if threads are running at exit.
|
||
for th in threading.enumerate():
|
||
if th != threading.currentThread():
|
||
th.join()
|
||
|
||
sys.exit(main_ret)
|