Adding base RMs for ns-3
[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 socket
24 from optparse import OptionParser, SUPPRESS_HELP
25
26 from ns3wrapper import NS3Wrapper
27
28 class NS3WrapperMessage:
29     CREATE = "CREATE"
30     FACTORY = "FACTORY"
31     INVOKE = "INVOKE"
32     SET = "SET"
33     GET = "GET"
34     TRACE = "TRACE"
35     START = "START"
36     STOP = "STOP"
37     SHUTDOWN = "SHUTDOWN"
38
39 def handle_message(ns3_wrapper, msg, args):
40     if msg == NS3WrapperMessage.SHUTDOWN:
41         ns3_wrapper.shutdown()
42         return "BYEBYE"
43     
44     if msg == NS3WrapperMessage.STOP:
45         time = None
46         if args:
47             time = args[0]
48
49         ns3_wrapper.stop(time=time)
50         return "STOPPED"
51
52     if msg == NS3WrapperMessage.START:
53         ns3_wrapper.start()
54         return "STARTED"
55
56     if msg == NS3WrapperMessage.CREATE:
57         clazzname = args.pop(0)
58         
59         uuid = ns3_wrapper.create(clazzname, *args)
60         return uuid
61
62     if msg == NS3WrapperMessage.FACTORY:
63         type_name = args.pop(0)
64         kwargs = args.pop(0)
65
66         uuid = ns3_wrapper.factory(type_name, **kwargs)
67         return uuid
68
69     if msg == NS3WrapperMessage.INVOKE:
70         uuid = args.pop(0)
71         operation = args.pop(0)
72         
73         uuid = ns3_wrapper.invoke(uuid, operation, *args)
74         return uuid
75
76     if msg == NS3WrapperMessage.GET:
77         uuid = args.pop(0)
78         name = args.pop(0)
79
80         value = ns3_wrapper.get(uuid, name)
81         return value
82
83     if msg == NS3WrapperMessage.SET:
84         uuid = args.pop(0)
85         name = args.pop(0)
86         value = args.pop(0)
87
88         value = ns3_wrapper.set(uuid, name, value)
89         return value
90  
91     if msg == NS3WrapperMessage.TRACE:
92         return "NOT IMPLEMENTED"
93
94 def create_socket(socket_name):
95     sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
96     sock.bind(socket_name)
97     return sock
98
99 def recv_msg(conn):
100     msg = []
101     chunk = ''
102
103     while '\n' not in chunk:
104         try:
105             chunk = conn.recv(1024)
106         except (OSError, socket.error), e:
107             if e[0] != errno.EINTR:
108                 raise
109             # Ignore eintr errors
110             continue
111
112         if chunk:
113             msg.append(chunk)
114         else:
115             # empty chunk = EOF
116             break
117  
118     msg = ''.join(msg).split('\n')[0]
119
120     # The message might have arguments that will be appended
121     # as a '|' separated list after the message identifier
122     def decode(arg):
123         arg = base64.b64decode(arg).rstrip()
124         return cPickle.loads(arg)
125
126     dargs = map(decode, msg.split("|"))
127
128     # decoded message
129     dmsg = dargs.pop(0)
130
131     return (dmsg, dargs)
132
133 def send_reply(conn, reply):
134     encoded = base64.b64encode(cPickle.dumps(reply))
135     conn.send("%s\n" % encoded)
136
137 def get_options():
138     usage = ("usage: %prog -S <socket-name>")
139     
140     parser = OptionParser(usage = usage)
141
142     parser.add_option("-S", "--socket-name", dest="socket_name",
143         help = "Name for the unix socket used to interact with this process", 
144         default = "tap.sock", type="str")
145
146     (options, args) = parser.parse_args()
147     
148     return options.socket_name
149
150 def run_server(socket_name): 
151     ns3_wrapper = NS3Wrapper()
152
153     # create unix socket to receive instructions
154     sock = create_socket(socket_name)
155     sock.listen(0)
156
157     # wait for messages to arrive and process them
158     stop = False
159
160     while not stop:
161         conn, addr = sock.accept()
162         conn.settimeout(5)
163
164         try:
165             (msg, args) = recv_msg(conn)
166         except socket.timeout, e:
167             # Ingore time-out
168             continue
169
170         if not msg:
171             # Ignore - connection lost
172             break
173  
174         ns3_wrapper.logger.debug("Message received %s args %s" % ( msg, str(args)))
175
176         if msg == NS3WrapperMessage.SHUTDOWN:
177            stop = True
178    
179         reply = handle_message(ns3_wrapper, msg, args)
180
181         try:
182             send_reply(conn, reply)
183         except socket.error:
184             break
185
186 if __name__ == '__main__':
187             
188     socket_name = get_options()
189
190     run_server(socket_name)
191