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