tcptest now explicitly passes a hostname along to the server side (before this change...
[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 on {}:{}"
69                 .format(args.address, args.port),
70                 id='server')
71         show_network_status(id='server')
72         server = socketserver.TCPServer((args.address, args.port),
73                                         UppercaseRequestHandler)
74         try:
75             if args.timeout:
76                 t = threading.Thread(target=server.serve_forever)
77                 t.setDaemon(True)  # don't hang on exit
78                 t.start()
79                 time.sleep(args.timeout)
80                 sys.exit(0)
81             else:
82                 server.serve_forever()
83         except KeyboardInterrupt:
84             print('Bailing out on keyboard interrupt')
85             sys.exit(1)
86
87
88 class Ready:
89     """
90     A utility that does exit(0) iff network as perceived
91     from the sliver is ready. Designed to be run before Server,
92     so one can wait for the right conditions.
93     """
94     def main(self):
95         parser = ArgumentParser()
96         # by default use another port so we don't run into
97         # the SO_LINGER kind of trouble
98         parser.add_argument("-p", "--port", action="store", dest="port",
99                             type=int, default=9999, help="port number")
100         parser.add_argument("-a", "--address", action="store", dest="address",
101                             default=socket.gethostname(), help="address")
102         args = parser.parse_args()
103
104         myprint("==================== tcptest.py ready", id='ready')
105
106         def can_bind():
107             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
108             try:
109                 s.bind((args.address, args.port))
110                 return True
111             except Exception as e:
112                 print(e)
113                 return False
114
115         def eth0_has_ipv4():
116             command = "ip address show eth0 | grep -q ' inet '"
117             return subprocess.check_call(command, shell=True) == 0
118
119         sys.exit(0 if can_bind() and eth0_has_ipv4() else 1)
120
121
122 class Client:
123     """
124     Runs a client against a Server instance
125     """
126     def main(self):
127         parser = ArgumentParser()
128         parser.add_argument("-p", "--port", action="store", dest="port",
129                             type=int, default=10000, help="port number")
130         parser.add_argument("-a", "--address", action="store", dest="address",
131                             default=socket.gethostname(), help="address")
132         parser.add_argument("-s", "--sleep", action="store", dest="sleep",
133                             type=int, default=1, help="sleep seconds")
134         parser.add_argument("-l", "--loops", action="store", dest="loops",
135                             type=int, default=1, help="iteration loops")
136
137         args = parser.parse_args()
138         if not args:
139             parser.print_help()
140             sys.exit(1)
141
142         myprint("==================== tcptest.py client -> {}:{}"
143                 .format(args.address, args.port),
144                 id='client')
145         result = True
146         for i in range(1, args.loops+1):
147             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
148             s.connect((args.address, args.port))
149             mout = i*b'ping_out ' + b'\n'
150             min =  i*b'PING_in  ' + b'\n'
151             if s.send(mout) != len(mout):
152                 myprint("cannot send {}".format(mout.strip()))
153                 result = False
154                 break
155             line = s.recv(len(min))
156             if line is not line:
157                 myprint("unexpected reception\ngot:{}\nexpected: {}"
158                         .format(line, min))
159                 result = False
160             else:
161                 myprint("OK:{}".format(mout.strip()))
162             # leave the connection open, but the last (so 1 iter returns fast)
163             if i != args.loops:
164                 time.sleep(args.sleep)
165             myprint("disconnecting")
166             s.close()
167         myprint("Done")
168         exit_return = 0
169         if not result:
170             exit_return = 1
171         sys.exit(exit_return)
172
173
174 if __name__ == '__main__':
175     for arg in sys.argv[1:]:
176         if arg.find("client") >= 0:
177             sys.argv.remove(arg)
178             Client().main()
179         elif arg.find("server") >= 0:
180             sys.argv.remove(arg)
181             Server().main()
182         elif arg.find("ready") >= 0:
183             sys.argv.remove(arg)
184             Ready().main()
185     print('you must specify either --client or --server')
186     sys.exit(1)