Adding linux ns3 server unit test
[nepi.git] / src / nepi / resources / ns3 / ns3server.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2014 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation, either version 3 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19
20 import base64
21 import cPickle
22 import errno
23 import logging
24 import os
25 import socket
26
27 from optparse import OptionParser, SUPPRESS_HELP
28
29 from ns3wrapper import NS3Wrapper
30
31 class NS3WrapperMessage:
32     CREATE = "CREATE"
33     FACTORY = "FACTORY"
34     INVOKE = "INVOKE"
35     SET = "SET"
36     GET = "GET"
37     TRACE = "TRACE"
38     START = "START"
39     STOP = "STOP"
40     SHUTDOWN = "SHUTDOWN"
41
42 def handle_message(ns3_wrapper, msg, args):
43     if msg == NS3WrapperMessage.SHUTDOWN:
44         ns3_wrapper.shutdown()
45         
46         ns3_wrapper.logger.debug("SHUTDOWN")
47         
48         return "BYEBYE"
49     
50     if msg == NS3WrapperMessage.STOP:
51         time = None
52         if args:
53             time = args[0]
54
55         ns3_wrapper.logger.debug("STOP time=%s" % str(time))
56
57         ns3_wrapper.stop(time=time)
58         return "STOPPED"
59
60     if msg == NS3WrapperMessage.START:
61         ns3_wrapper.logger.debug("START") 
62
63         ns3_wrapper.start()
64         return "STARTED"
65
66     if msg == NS3WrapperMessage.CREATE:
67         clazzname = args.pop(0)
68         
69         ns3_wrapper.logger.debug("CREATE %s %s" % (clazzname, str(args)))
70
71         uuid = ns3_wrapper.create(clazzname, *args)
72         return uuid
73
74     if msg == NS3WrapperMessage.FACTORY:
75         type_name = args.pop(0)
76         kwargs = args.pop(0)
77
78         ns3_wrapper.logger.debug("FACTORY %s %s" % (type_name, str(kwargs)))
79
80         uuid = ns3_wrapper.factory(type_name, **kwargs)
81         return uuid
82
83     if msg == NS3WrapperMessage.INVOKE:
84         uuid = args.pop(0)
85         operation = args.pop(0)
86         
87         ns3_wrapper.logger.debug("INVOKE %s %s %s" % (uuid, operation, str(args)))
88     
89         uuid = ns3_wrapper.invoke(uuid, operation, *args)
90         return uuid
91
92     if msg == NS3WrapperMessage.GET:
93         uuid = args.pop(0)
94         name = args.pop(0)
95
96         ns3_wrapper.logger.debug("GET %s %s" % (uuid, name))
97
98         value = ns3_wrapper.get(uuid, name)
99         return value
100
101     if msg == NS3WrapperMessage.SET:
102         uuid = args.pop(0)
103         name = args.pop(0)
104         value = args.pop(0)
105
106         ns3_wrapper.logger.debug("SET %s %s" % (uuid, name, str(value)))
107
108         value = ns3_wrapper.set(uuid, name, value)
109         return value
110  
111     if msg == NS3WrapperMessage.TRACE:
112         ns3_wrapper.logger.debug("TRACE") 
113         return "NOT IMPLEMENTED"
114
115 def create_socket(socket_name):
116     sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
117     sock.bind(socket_name)
118     return sock
119
120 def recv_msg(conn):
121     msg = []
122     chunk = ''
123
124     while '\n' not in chunk:
125         try:
126             chunk = conn.recv(1024)
127         except (OSError, socket.error), e:
128             if e[0] != errno.EINTR:
129                 raise
130             # Ignore eintr errors
131             continue
132
133         if chunk:
134             msg.append(chunk)
135         else:
136             # empty chunk = EOF
137             break
138  
139     msg = ''.join(msg).split('\n')[0]
140
141     # The message might have arguments that will be appended
142     # as a '|' separated list after the message identifier
143     def decode(arg):
144         arg = base64.b64decode(arg).rstrip()
145         return cPickle.loads(arg)
146
147     dargs = map(decode, msg.split("|"))
148
149     # decoded message
150     dmsg = dargs.pop(0)
151
152     return (dmsg, dargs)
153
154 def send_reply(conn, reply):
155     encoded = base64.b64encode(cPickle.dumps(reply))
156     conn.send("%s\n" % encoded)
157
158 def get_options():
159     usage = ("usage: %prog -S <socket-name> -L <NS_LOG> -v ")
160     
161     parser = OptionParser(usage = usage)
162
163     parser.add_option("-S", "--socket-name", dest="socket_name",
164         help = "Name for the unix socket used to interact with this process", 
165         default = "tap.sock", type="str")
166
167     parser.add_option("-L", "--ns-log", dest="ns_log",
168         help = "NS_LOG environmental variable to be set", 
169         default = "", type="str")
170
171     parser.add_option("-v", "--verbose",
172         help="Print debug output",
173         action="store_true", 
174         dest="verbose", default=False)
175
176     (options, args) = parser.parse_args()
177     
178     return options.socket_name, options.verbose, options.ns_log
179
180 def run_server(socket_name, verbose = False, ns_log = None):
181
182     level = logging.DEBUG if verbose else logging.INFO
183
184     # Sets NS_LOG environmental variable for NS debugging
185     if ns_log:
186         os.environ["NS_LOG"] = ns_log
187
188     ###### ns-3 wrapper instantiation
189
190     ns3_wrapper = NS3Wrapper(loglevel=level)
191
192     # create unix socket to receive instructions
193     sock = create_socket(socket_name)
194     sock.listen(0)
195
196     # wait for messages to arrive and process them
197     stop = False
198
199     while not stop:
200         conn, addr = sock.accept()
201         conn.settimeout(5)
202
203         try:
204             (msg, args) = recv_msg(conn)
205         except socket.timeout, e:
206             # Ingore time-out
207             continue
208
209         if not msg:
210             # Ignore - connection lost
211             break
212
213         if msg == NS3WrapperMessage.SHUTDOWN:
214            stop = True
215    
216         reply = handle_message(ns3_wrapper, msg, args)
217
218         try:
219             send_reply(conn, reply)
220         except socket.error:
221             break
222
223 if __name__ == '__main__':
224             
225     (socket_name, verbose, ns_log) = get_options()
226
227     run_server(socket_name, verbose, ns_log)
228