88c2b0d046261a93132e69b8cb3ec013570b22c3
[tests.git] / system / tcptest.py
1 #!/usr/bin/env python3
2
3 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
4 # Copyright (C) 2010 INRIA
5 #
6
7 # this is a small and simple standalone utility
8 # designed to run in slice-space
9 # we keep this in python2 for now until python3
10 # can be taken for granted in sliceimage
11
12 # pylint: disable=c0111, c0103, w0622, r0903, r0201, w0703
13
14 import sys
15 import time
16 import subprocess
17 import socket
18 import socketserver
19 import threading
20 from argparse import ArgumentParser
21
22
23 def myprint(message, id='client'):
24     now = time.strftime("%H:%M:%S", time.localtime())
25     print("* {now} ({id}) -- {message}"
26           .format(now=now, id=id, message=message))
27     sys.stdout.flush()
28
29
30 def show_network_status(id):
31     myprint("ip address show", id=id)
32     subprocess.call(['ip', 'address', 'show'])
33     myprint("ip route show", id=id)
34     subprocess.call(['ip', 'route', 'show'])
35
36
37 class EchoRequestHandler(socketserver.StreamRequestHandler):
38     def handle(self):
39         line = self.rfile.readline()
40         self.wfile.write(line)
41
42
43 class UppercaseRequestHandler(socketserver.StreamRequestHandler):
44     def handle(self):
45         line = self.rfile.readline()
46         self.wfile.write(line.upper())
47
48
49 class Server:
50     """
51     A TCP server, running for some finite amount of time
52     """
53     def main(self):
54         parser = ArgumentParser()
55         parser.add_argument("-p", "--port", type=int, default=10000,
56                             action="store", dest="port",
57                             help="port number")
58         parser.add_argument("-a", "--address", action="store", dest="address",
59                             default=socket.gethostname(), help="address")
60         parser.add_argument("-t", "--timeout", action="store", dest="timeout",
61                             type=int, default="0")
62         args = parser.parse_args()
63
64         if not args:
65             parser.print_help()
66             sys.exit(1)
67
68         myprint("==================== tcptest.py server", id='server')
69         show_network_status(id='server')
70         server = socketserver.TCPServer((args.address, args.port),
71                                         UppercaseRequestHandler)
72         try:
73             if args.timeout:
74                 t = threading.Thread(target=server.serve_forever)
75                 t.setDaemon(True)  # don't hang on exit
76                 t.start()
77                 time.sleep(args.timeout)
78                 sys.exit(0)
79             else:
80                 server.serve_forever()
81         except KeyboardInterrupt:
82             print('Bailing out on keyboard interrupt')
83             sys.exit(1)
84
85
86 class Ready:
87     """
88     A utility that does exit(0) iff network as perceived
89     from the sliver is ready. Designed to be run before Server,
90     so one can wait for the right conditions.
91     """
92     def main(self):
93         parser = ArgumentParser()
94         # by default use another port so we don't run into
95         # the SO_LINGER kind of trouble
96         parser.add_argument("-p", "--port", action="store", dest="port",
97                             type=int, default=9999, help="port number")
98         parser.add_argument("-a", "--address", action="store", dest="address",
99                             default=socket.gethostname(), help="address")
100         args = parser.parse_args()
101
102         myprint("==================== tcptest.py ready", id='ready')
103
104         def can_bind():
105             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106             try:
107                 s.bind((args.address, args.port))
108                 return True
109             except Exception as e:
110                 print(e)
111                 return False
112
113         def eth0_has_ipv4():
114             command = "ip address show eth0 | grep -q ' inet '"
115             return subprocess.check_call(command, shell=True) == 0
116
117         sys.exit(0 if can_bind() and eth0_has_ipv4() else 1)
118
119
120 class Client:
121     """
122     Runs a client against a Server instance
123     """
124     def main(self):
125         parser = ArgumentParser()
126         parser.add_argument("-p", "--port", action="store", dest="port",
127                             type=int, default=10000, help="port number")
128         parser.add_argument("-a", "--address", action="store", dest="address",
129                             default=socket.gethostname(), help="address")
130         parser.add_argument("-s", "--sleep", action="store", dest="sleep",
131                             type=int, default=1, help="sleep seconds")
132         parser.add_argument("-l", "--loops", action="store", dest="loops",
133                             type=int, default=1, help="iteration loops")
134
135         args = parser.parse_args()
136         if not args:
137             parser.print_help()
138             sys.exit(1)
139
140         myprint("==================== tcptest.py client", id='client')
141         result = True
142         for i in range(1, args.loops+1):
143             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
144             s.connect((args.address, args.port))
145             mout = i*b'ping_out ' + b'\n'
146             min =  i*b'PING_in  ' + b'\n'
147             if s.send(mout) != len(mout):
148                 myprint("cannot send {}".format(mout.strip()))
149                 result = False
150                 break
151             line = s.recv(len(min))
152             if line is not line:
153                 myprint("unexpected reception\ngot:{}\nexpected: {}"
154                         .format(line, min))
155                 result = False
156             else:
157                 myprint("OK:{}".format(mout.strip()))
158             # leave the connection open, but the last (so 1 iter returns fast)
159             if i != args.loops:
160                 time.sleep(args.sleep)
161             myprint("disconnecting")
162             s.close()
163         myprint("Done")
164         exit_return = 0
165         if not result:
166             exit_return = 1
167         sys.exit(exit_return)
168
169
170 if __name__ == '__main__':
171     for arg in sys.argv[1:]:
172         if arg.find("client") >= 0:
173             sys.argv.remove(arg)
174             Client().main()
175         elif arg.find("server") >= 0:
176             sys.argv.remove(arg)
177             Server().main()
178         elif arg.find("ready") >= 0:
179             sys.argv.remove(arg)
180             Ready().main()
181     print('you must specify either --client or --server')
182     sys.exit(1)