applied the except and raise fixers to the master branch to close the gap with py3
[nepi.git] / src / nepi / resources / linux / ccn / fibentry.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 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 version 2 as
7 #    published by the Free Software Foundation;
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
18
19 from nepi.execution.attribute import Attribute, Flags, Types
20 from nepi.execution.trace import Trace, TraceAttr
21 from nepi.execution.resource import clsinit_copy, ResourceState, \
22     ResourceAction
23 from nepi.resources.linux.application import LinuxApplication
24 from nepi.resources.linux.ccn.ccnd import LinuxCCND
25 from nepi.util.timefuncs import tnow
26
27 import os
28 import socket
29 import time
30
31
32 # TODO: Add rest of options for ccndc!!!
33 #       Implement ENTRY DELETE!!
34
35 @clsinit_copy
36 class LinuxFIBEntry(LinuxApplication):
37     _rtype = "linux::FIBEntry"
38
39     @classmethod
40     def _register_attributes(cls):
41         uri = Attribute("uri",
42                 "URI prefix to match and route for this FIB entry",
43                 default = "ccnx:/",
44                 flags = Flags.Design)
45
46         protocol = Attribute("protocol",
47                 "Transport protocol used in network connection to peer "
48                 "for this FIB entry. One of 'udp' or 'tcp'.",
49                 type = Types.Enumerate, 
50                 default = "udp",
51                 allowed = ["udp", "tcp"],
52                 flags = Flags.Design)
53
54         host = Attribute("host",
55                 "Peer hostname used in network connection for this FIB entry. ",
56                 flags = Flags.Design)
57
58         port = Attribute("port",
59                 "Peer port address used in network connection to peer "
60                 "for this FIB entry.",
61                 flags = Flags.Design)
62
63         ip = Attribute("ip",
64                 "Peer host public IP used in network connection for this FIB entry. ",
65                 flags = Flags.Design)
66
67         cls._register_attribute(uri)
68         cls._register_attribute(protocol)
69         cls._register_attribute(host)
70         cls._register_attribute(port)
71         cls._register_attribute(ip)
72
73     @classmethod
74     def _register_traces(cls):
75         ping = Trace("ping", "Ping to the peer end")
76         mtr = Trace("mtr", "Mtr to the peer end")
77         traceroute = Trace("traceroute", "Tracerout to the peer end")
78
79         cls._register_trace(ping)
80         cls._register_trace(mtr)
81         cls._register_trace(traceroute)
82
83     def __init__(self, ec, guid):
84         super(LinuxFIBEntry, self).__init__(ec, guid)
85         self._home = "fib-%s" % self.guid
86         self._ping = None
87         self._traceroute = None
88         self._ccnd = None
89
90     @property
91     def ccnd(self):
92         if not self._ccnd:
93             ccnd = self.get_connected(LinuxCCND.get_rtype())
94             if ccnd: 
95                 self._ccnd = ccnd[0]
96             
97         return self._ccnd
98
99     @property
100     def ping(self):
101         if not self._ping:
102             from nepi.resources.linux.ping import LinuxPing
103             ping = self.get_connected(LinuxPing.get_rtype())
104             if ping: 
105                 self._ping = ping[0]
106             
107         return self._ping
108
109     @property
110     def traceroute(self):
111         if not self._traceroute:
112             from nepi.resources.linux.traceroute import LinuxTraceroute
113             traceroute = self.get_connected(LinuxTraceroute.get_rtype())
114             if traceroute: 
115                 self._traceroute = traceroute[0]
116             
117         return self._traceroute
118
119     @property
120     def node(self):
121         if self.ccnd: return self.ccnd.node
122         return None
123
124     def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
125         if name == "ping":
126             if not self.ping:
127                 return None
128             return self.ec.trace(self.ping.guid, "stdout", attr, block, offset)
129
130         if name == "traceroute":
131             if not self.traceroute:
132                 return None
133             return self.ec.trace(self.traceroute.guid, "stdout", attr, block, offset)
134
135         return super(LinuxFIBEntry, self).trace(name, attr, block, offset)
136     
137     def do_deploy(self):
138         # Wait until associated ccnd is provisioned
139         if not self.ccnd or self.ccnd.state < ResourceState.READY:
140             # ccnr needs to wait until ccnd is deployed and running
141             self.ec.schedule(self.reschedule_delay, self.deploy)
142         else:
143             if not self.get("ip"):
144                 host = self.get("host")
145                 ip = socket.gethostbyname(host)
146                 self.set("ip", ip)
147
148             if not self.get("command"):
149                 self.set("command", self._start_command)
150
151             if not self.get("env"):
152                 self.set("env", self._environment)
153
154             command = self.get("command")
155
156             self.info("Deploying command '%s' " % command)
157
158             self.do_discover()
159             self.do_provision()
160             self.configure()
161
162             self.set_ready()
163
164     def upload_start_command(self):
165         command = self.get("command")
166         env = self.get("env")
167
168         # We want to make sure the FIB entries are created
169         # before the experiment starts.
170         # Run the command as a bash script in the background, 
171         # in the host ( but wait until the command has
172         # finished to continue )
173         env = env and self.replace_paths(env)
174         command = self.replace_paths(command)
175
176         # ccndc seems to return exitcode OK even if a (dns) error
177         # occurred, so we need to account for this case here. 
178         (out, err), proc = self.execute_command(command, 
179                 env, blocking = True)
180
181         if proc.poll():
182             msg = "Failed to execute command"
183             self.error(msg, out, err)
184             raise RuntimeError(msg)
185         
186     def configure(self):
187         if self.trace_enabled("ping") and not self.ping:
188             self.info("Configuring PING trace")
189             ping = self.ec.register_resource("linux::Ping")
190             self.ec.set(ping, "printTimestamp", True)
191             self.ec.set(ping, "target", self.get("host"))
192             self.ec.set(ping, "earlyStart", True)
193             self.ec.register_connection(ping, self.node.guid)
194             self.ec.register_connection(ping, self.guid)
195             # schedule ping deploy
196             self.ec.deploy(guids=[ping], group = self.deployment_group)
197
198         if self.trace_enabled("traceroute") and not self.traceroute:
199             self.info("Configuring TRACEROUTE trace")
200             traceroute = self.ec.register_resource("linux::Traceroute")
201             self.ec.set(traceroute, "printTimestamp", True)
202             self.ec.set(traceroute, "continuous", True)
203             self.ec.set(traceroute, "target", self.get("host"))
204             self.ec.set(traceroute, "earlyStart", True)
205             self.ec.register_connection(traceroute, self.node.guid)
206             self.ec.register_connection(traceroute, self.guid)
207             # schedule mtr deploy
208             self.ec.deploy(guids=[traceroute], group = self.deployment_group)
209
210     def do_start(self):
211         if self.state == ResourceState.READY:
212             command = self.get("command")
213             self.info("Starting command '%s'" % command)
214
215             self.set_started()
216         else:
217             msg = " Failed to execute command '%s'" % command
218             self.error(msg, out, err)
219             raise RuntimeError(msg)
220
221     def do_stop(self):
222         command = self.get('command')
223         env = self.get('env')
224         
225         if self.state == ResourceState.STARTED:
226             self.info("Stopping command '%s'" % command)
227
228             command = self._stop_command
229             (out, err), proc = self.execute_command(command, env,
230                     blocking = True)
231
232             self.set_stopped()
233
234             if err:
235                 msg = " Failed to execute command '%s'" % command
236                 self.error(msg, out, err)
237                 raise RuntimeError(msg)
238
239     @property
240     def _start_command(self):
241         uri = self.get("uri") or ""
242         protocol = self.get("protocol") or ""
243         ip = self.get("ip") or "" 
244         port = self.get("port") or ""
245
246         # add ccnx:/example.com/ udp 224.0.0.204 52428
247         return "ccndc add %(uri)s %(protocol)s %(host)s %(port)s" % ({
248             "uri" : uri,
249             "protocol": protocol,
250             "host": ip,
251             "port": port
252             })
253
254     @property
255     def _stop_command(self):
256         uri = self.get("uri") or ""
257         protocol = self.get("protocol") or ""
258         ip = self.get("ip") or ""
259         port = self.get("port") or ""
260
261         # add ccnx:/example.com/ udp 224.0.0.204 52428
262         return "ccndc del %(uri)s %(protocol)s %(host)s %(port)s" % ({
263             "uri" : uri,
264             "protocol": protocol,
265             "host": ip,
266             "port": port
267             })
268
269     @property
270     def _environment(self):
271         return self.ccnd.path
272        
273     def valid_connection(self, guid):
274         # TODO: Validate!
275         return True
276