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