re-arrenging ns-3 classes
[nepi.git] / src / nepi / resources / linux / ns3 / ns3simulator.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.resources.linux.node import OSType
26 from nepi.util.timefuncs import tnow, tdiffsec
27 from nepi.resources.ns3.ns3simulator import NS3Simulator
28 from nepi.resources.linux.ns3.ns3client import LinuxNS3Client
29
30 import os
31
32 @clsinit_copy
33 class LinuxNS3Simulator(LinuxApplication, NS3Simulator):
34     _rtype = "LinuxNS3Simulator"
35
36     @classmethod
37     def _register_attributes(cls):
38         max_rte = Attribute("maxRteMicrosec",
39             "Sets the CCND_MAX_RTE_MICROSEC environmental variable. ",
40             flags = Flags.ExecReadOnly)
41
42         cls._register_attribute(debug)
43
44     def __init__(self, ec, guid):
45         super(LinuxApplication, self).__init__(ec, guid)
46         super(NS3Simulator, self).__init__()
47
48         self._home = "ns3-simu-%s" % self.guid
49         
50         # TODO: Create socket!!
51         self._client = LinuxNS3Client(socket_name)
52
53
54     def do_deploy(self):
55         if not self.node or self.node.state < ResourceState.READY:
56             self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
57             
58             # ccnd needs to wait until node is deployed and running
59             self.ec.schedule(reschedule_delay, self.deploy)
60         else:
61             if not self.get("command"):
62                 self.set("command", self._start_command)
63             
64             if not self.get("depends"):
65                 self.set("depends", self._dependencies)
66
67             if not self.get("sources"):
68                 self.set("sources", self._sources)
69
70             sources = self.get("sources")
71             source = sources.split(" ")[0]
72             basename = os.path.basename(source)
73             self._version = ( basename.strip().replace(".tar.gz", "")
74                     .replace(".tar","")
75                     .replace(".gz","")
76                     .replace(".zip","") )
77
78             if not self.get("build"):
79                 self.set("build", self._build)
80
81             if not self.get("install"):
82                 self.set("install", self._install)
83
84             if not self.get("env"):
85                 self.set("env", self._environment)
86
87             command = self.get("command")
88
89             self.info("Deploying command '%s' " % command)
90
91             self.do_discover()
92             self.do_provision()
93
94             self.debug("----- READY ---- ")
95             self.set_ready()
96
97     def upload_start_command(self):
98         command = self.get("command")
99         env = self.get("env")
100
101         # We want to make sure the ccnd is running
102         # before the experiment starts.
103         # Run the command as a bash script in background,
104         # in the host ( but wait until the command has
105         # finished to continue )
106         env = self.replace_paths(env)
107         command = self.replace_paths(command)
108
109         shfile = os.path.join(self.app_home, "start.sh")
110         self.node.run_and_wait(command, self.run_home,
111                 shfile = shfile,
112                 overwrite = False,
113                 env = env,
114                 raise_on_error = True)
115
116     def do_start(self):
117         if self.state == ResourceState.READY:
118             command = self.get("command")
119             self.info("Starting command '%s'" % command)
120
121             self.set_started()
122         else:
123             msg = " Failed to execute command '%s'" % command
124             self.error(msg, out, err)
125             raise RuntimeError, msg
126
127     def do_stop(self):
128         command = self.get('command') or ''
129         
130         if self.state == ResourceState.STARTED:
131             self.info("Stopping command '%s'" % command)
132
133             command = "ccndstop"
134             env = self.get("env") 
135
136             # replace application specific paths in the command
137             command = self.replace_paths(command)
138             env = env and self.replace_paths(env)
139
140             # Upload the command to a file, and execute asynchronously
141             shfile = os.path.join(self.app_home, "stop.sh")
142             self.node.run_and_wait(command, self.run_home,
143                         shfile = shfile,
144                         overwrite = False,
145                         env = env,
146                         pidfile = "ccndstop_pidfile", 
147                         ecodefile = "ccndstop_exitcode", 
148                         stdout = "ccndstop_stdout", 
149                         stderr = "ccndstop_stderr")
150
151             self.set_stopped()
152     
153     @property
154     def state(self):
155         # First check if the ccnd has failed
156         state_check_delay = 0.5
157         if self._state == ResourceState.STARTED and \
158                 tdiffsec(tnow(), self._last_state_check) > state_check_delay:
159             (out, err), proc = self._ccndstatus()
160
161             retcode = proc.poll()
162
163             if retcode == 1 and err.find("No such file or directory") > -1:
164                 # ccnd is not running (socket not found)
165                 self.set_stopped()
166             elif retcode:
167                 # other errors ...
168                 msg = " Failed to execute command '%s'" % self.get("command")
169                 self.error(msg, out, err)
170                 self.fail()
171
172             self._last_state_check = tnow()
173
174         return self._state
175
176     def _ccndstatus(self):
177         env = self.get('env') or ""
178         environ = self.node.format_environment(env, inline = True)
179         command = environ + " ccndstatus"
180         command = self.replace_paths(command)
181     
182         return self.node.execute(command)
183
184     @property
185     def _start_command(self):
186         return "ccndstart"
187
188     @property
189     def _dependencies(self):
190         if self.node.use_rpm:
191             return ( " autoconf openssl-devel  expat-devel libpcap-devel "
192                 " ecryptfs-utils-devel libxml2-devel automake gawk " 
193                 " gcc gcc-c++ git pcre-devel make ")
194         elif self.node.use_deb:
195             return ( " autoconf libssl-dev libexpat-dev libpcap-dev "
196                 " libecryptfs0 libxml2-utils automake gawk gcc g++ "
197                 " git-core pkg-config libpcre3-dev make ")
198         return ""
199
200     @property
201     def _sources(self):
202         return "http://www.ccnx.org/releases/ccnx-0.7.2.tar.gz"
203
204     @property
205     def _build(self):
206         sources = self.get("sources").split(" ")[0]
207         sources = os.path.basename(sources)
208
209         return (
210             # Evaluate if ccnx binaries are already installed
211             " ( "
212                 " test -f ${BIN}/%(version)s/ccnd && "
213                 " echo 'binaries found, nothing to do' "
214             " ) || ( "
215             # If not, untar and build
216                 " ( "
217                     " mkdir -p ${SRC}/%(version)s && "
218                     " tar xf ${SRC}/%(sources)s --strip-components=1 -C ${SRC}/%(version)s "
219                  " ) && "
220                     "cd ${SRC}/%(version)s && "
221                     # Just execute and silence warnings...
222                     " ( ./configure && make ) "
223              " )") % ({ 'sources': sources,
224                         'version': self.version
225                  })
226
227     @property
228     def _install(self):
229         return (
230             # Evaluate if ccnx binaries are already installed
231             " ( "
232                 " test -f ${BIN}/%(version)s/ccnd && "
233                 " echo 'binaries found, nothing to do' "
234             " ) || ( "
235             # If not, install
236                 "  mkdir -p ${BIN}/%(version)s && "
237                 "  mv ${SRC}/%(version)s/bin/* ${BIN}/%(version)s/ "
238             " )"
239             ) % ({ 'version': self.version
240                  })
241
242     @property
243     def _environment(self):
244         envs = dict({
245             "debug": "CCND_DEBUG",
246             "port": "CCN_LOCAL_PORT",
247             "sockname" : "CCN_LOCAL_SOCKNAME",
248             "capacity" : "CCND_CAP",
249             "mtu" : "CCND_MTU",
250             "dataPauseMicrosec" : "CCND_DATA_PAUSE_MICROSEC",
251             "defaultTimeToStale" : "CCND_DEFAULT_TIME_TO_STALE",
252             "maxTimeToStale" : "CCND_MAX_TIME_TO_STALE",
253             "maxRteMicrosec" : "CCND_MAX_RTE_MICROSEC",
254             "keyStoreDirectory" : "CCND_KEYSTORE_DIRECTORY",
255             "listenOn" : "CCND_LISTEN_ON",
256             "autoreg" : "CCND_AUTOREG",
257             "prefix" : "CCND_PREFIX",
258             })
259
260         env = self.path 
261         env += " ".join(map(lambda k: "%s=%s" % (envs.get(k), str(self.get(k))) \
262             if self.get(k) else "", envs.keys()))
263         
264         return env            
265
266     def valid_connection(self, guid):
267         # TODO: Validate!
268         return True
269