Remote ns-3 with ping working
[nepi.git] / src / nepi / resources / linux / ns3 / ns3simulation.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 from nepi.execution.attribute import Attribute, Flags, Types
21 from nepi.execution.trace import Trace, TraceAttr
22 from nepi.execution.resource import ResourceManager, clsinit_copy, \
23         ResourceState, reschedule_delay
24 from nepi.resources.linux.application import LinuxApplication
25 from nepi.util.timefuncs import tnow, tdiffsec
26 from nepi.resources.ns3.ns3simulation import NS3Simulation
27 from nepi.resources.linux.ns3.ns3client import LinuxNS3Client
28
29 import os
30
31 @clsinit_copy
32 class LinuxNS3Simulation(LinuxApplication, NS3Simulation):
33     _rtype = "LinuxNS3Simulation"
34
35     @classmethod
36     def _register_attributes(cls):
37         ns_log = Attribute("nsLog",
38             "NS_LOG environment variable. " \
39                     " Will only generate output if ns-3 is compiled in DEBUG mode. ",
40             flags = Flags.Design)
41
42         verbose = Attribute("verbose",
43             "True to output debugging info from the ns3 client-server communication",
44             type = Types.Bool,
45             flags = Flags.Design)
46
47         cls._register_attribute(ns_log)
48         
49         cls._register_attribute(verbose)
50
51     def __init__(self, ec, guid):
52         LinuxApplication.__init__(self, ec, guid)
53         NS3Simulation.__init__(self)
54
55         self._client = None
56         self._home = "ns3-simu-%s" % self.guid
57         self._socket_name = "ns3simu-%s" % os.urandom(8).encode('hex')
58
59     @property
60     def socket_name(self):
61         return self._socket_name
62
63     @property
64     def remote_socket(self):
65         return os.path.join(self.run_home, self.socket_name)
66
67     @property
68     def local_socket(self):
69         if self.node.get('hostname') in ['localhost', '127.0.0.01']:
70             return self.remote_socket
71
72         return os.path.join("/", "tmp", self.socket_name)
73
74     def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
75         self._client.flush() 
76         return LinuxApplication.trace(self, name, attr, block, offset)
77
78     def upload_sources(self):
79         self.node.mkdir(os.path.join(self.node.src_dir, "ns3wrapper"))
80
81         # upload ns3 wrapper python script
82         ns3_wrapper = os.path.join(os.path.dirname(__file__), "..", "..", "ns3", 
83                 "ns3wrapper.py")
84
85         self.node.upload(ns3_wrapper,
86                 os.path.join(self.node.src_dir, "ns3wrapper", "ns3wrapper.py"),
87                 overwrite = False)
88
89         # upload ns3_server python script
90         ns3_server = os.path.join(os.path.dirname(__file__), "..", "..", "ns3",
91                 "ns3server.py")
92
93         self.node.upload(ns3_server,
94                 os.path.join(self.node.src_dir, "ns3wrapper", "ns3server.py"),
95                 overwrite = False)
96
97         if self.node.use_rpm:
98             # upload pygccxml sources
99             pygccxml_tar = os.path.join(os.path.dirname(__file__), "dependencies",
100                     "%s.tar.gz" % self.pygccxml_version)
101
102             self.node.upload(pygccxml_tar,
103                     os.path.join(self.node.src_dir, "%s.tar.gz" % self.pygccxml_version),
104                     overwrite = False)
105
106     def upload_start_command(self):
107         command = self.get("command")
108         env = self.get("env")
109
110         # We want to make sure the ccnd is running
111         # before the experiment starts.
112         # Run the command as a bash script in background,
113         # in the host ( but wait until the command has
114         # finished to continue )
115         env = self.replace_paths(env)
116         command = self.replace_paths(command)
117
118         shfile = os.path.join(self.app_home, "start.sh")
119         self.node.upload_command(command, 
120                     shfile = shfile,
121                     env = env,
122                     overwrite = True)
123
124         # Run the ns3wrapper 
125         self._run_in_background()
126
127     def do_deploy(self):
128         if not self.node or self.node.state < ResourceState.READY:
129             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
130             
131             # ccnd needs to wait until node is deployed and running
132             self.ec.schedule(reschedule_delay, self.deploy)
133         else:
134             if not self.get("command"):
135                 self.set("command", self._start_command)
136             
137             if not self.get("depends"):
138                 self.set("depends", self._dependencies)
139
140             if not self.get("build"):
141                 self.set("build", self._build)
142
143             if not self.get("install"):
144                 self.set("install", self._install)
145
146             if not self.get("env"):
147                 self.set("env", self._environment)
148
149             self.do_discover()
150             self.do_provision()
151
152             # Create client
153             self._client = LinuxNS3Client(self) 
154
155             self.set_ready()
156
157     def do_start(self):
158         """ Starts simulation execution
159
160         """
161         self.info("Starting ns-3 simulation")
162
163         if self.state == ResourceState.READY:
164             self._client.start() 
165             self.set_started()
166         else:
167             msg = " Failed to execute command '%s'" % command
168             self.error(msg, out, err)
169             raise RuntimeError, msg
170
171     def do_stop(self):
172         """ Stops simulation execution
173
174         """
175         if self.state == ResourceState.STARTED:
176             self._client.stop() 
177             self._client.shutdown()
178             LinuxApplication.do_stop(self)
179
180     def do_release(self):
181         self.info("Releasing resource")
182
183         tear_down = self.get("tearDown")
184         if tear_down:
185             self.node.execute(tear_down)
186
187         self.do_stop()
188         
189         super(LinuxApplication, self).do_release()
190
191     @property
192     def _start_command(self):
193         command = [] 
194         command.append("PYTHONPATH=$PYTHONPATH:${SRC}/ns3wrapper/")
195
196         command.append("python ${SRC}/ns3wrapper/ns3server.py -S %s" % self.remote_socket )
197
198         ns_log = self.get("nsLog")
199         if ns_log:
200             command.append("-L %s" % ns_log)
201
202         if self.get("verbose"):
203             command.append("-v")
204
205         command = " ".join(command)
206         return command
207
208     @property
209     def _dependencies(self):
210         if self.node.use_rpm:
211             return ( " gcc gcc-c++ python python-devel mercurial bzr tcpdump socat gccxml")
212         elif self.node.use_deb:
213             return ( " gcc g++ python python-dev mercurial bzr tcpdump socat gccxml python-pygccxml")
214         return ""
215
216     @property
217     def ns3_repo(self):
218        return "http://code.nsnam.org"
219
220     @property
221     def ns3_version(self):
222        return "ns-3.19"
223
224     @property
225     def pybindgen_version(self):
226        return "834"
227
228     @property
229     def pygccxml_version(self):
230        return "pygccxml-1.0.0"
231
232     @property
233     def _build(self):
234         return (
235                 # Test if ns-3 is alredy installed
236                 " ( "
237                 " (( "
238                 "  ( test -d ${SRC}/%(ns3_version)s ) || (test -d ${NS3BINDINGS:='None'} && test -d ${NS3LIBRARIES:='None'}) ) && "
239                 "  echo 'binaries found, nothing to do' )"
240                 " ) "
241                 "  || " 
242                 # If not, install ns-3 and its dependencies
243                 " (   "
244                 # Install pygccxml
245                 "   (   "
246                 "     ( "
247                 "       python -c 'import pygccxml' && "
248                 "       echo 'pygccxml not found' "
249                 "     ) "
250                 "      || "
251                 "     ( "
252                 "       tar xf ${SRC}/%(pygccxml_version)s.tar.gz -C ${SRC} && "
253                 "       cd ${SRC}/%(pygccxml_version)s && "
254                 "       sudo -S python setup.py install "
255                 "     ) "
256                 "   ) " 
257                 # Install pybindgen
258                 "  && "
259                 "   (   "
260                 "     ( "
261                 "       test -d ${BIN}/pybindgen && "
262                 "       echo 'binaries found, nothing to do' "
263                 "     ) "
264                 "      || "
265                 # If not, clone and build
266                 "      ( cd ${SRC} && "
267                 "        bzr checkout lp:pybindgen -r %(pybindgen_version)s && "
268                 "        cd ${SRC}/pybindgen && "
269                 "        ./waf configure && "
270                 "        ./waf "
271                 "      ) "
272                 "   ) " 
273                "  && "
274                 # Clone and build ns-3
275                 "  ( "
276                 "    hg clone %(ns3_repo)s/%(ns3_version)s ${SRC}/%(ns3_version)s && "
277                 "    cd ${SRC}/%(ns3_version)s && "
278                 "    ./waf configure -d optimized  && "
279                 "    ./waf "
280                 "   ) "
281                 " ) "
282              ) % ({ 
283                     'ns3_repo':  self.ns3_repo,       
284                     'ns3_version': self.ns3_version,
285                     'pybindgen_version': self.pybindgen_version,
286                     'pygccxml_version': self.pygccxml_version
287                  })
288
289     @property
290     def _install(self):
291         return (
292                  # Test if ns-3 is alredy cloned
293                 " ( "
294                 "  ( ( (test -d ${BIN}/%(ns3_version)s/build ) || "
295                 "    (test -d ${NS3BINDINGS:='None'} && test -d ${NS3LIBRARIES:='None'}) ) && "
296                 "    echo 'binaries found, nothing to do' )"
297                 " ) "
298                 " ||" 
299                 " (   "
300                  # If not, copy ns-3 build to bin
301                 "  mkdir -p ${BIN}/%(ns3_version)s && "
302                 "  mv ${SRC}/%(ns3_version)s/build ${BIN}/%(ns3_version)s/build "
303                 " )"
304              ) % ({ 
305                     'ns3_version': self.ns3_version
306                  })
307
308     @property
309     def _environment(self):
310         env = []
311         env.append("NS3BINDINGS=${NS3BINDINGS:=${BIN}/%(ns3_version)s/build/bindings/python/}" % ({ 
312                     'ns3_version': self.ns3_version,
313                  }))
314         env.append("NS3LIBRARIES=${NS3LIBRARIES:=${BIN}/%(ns3_version)s/build/}" % ({ 
315                     'ns3_version': self.ns3_version,
316                  }))
317
318         return " ".join(env) 
319
320     def valid_connection(self, guid):
321         # TODO: Validate!
322         return True
323