Adding comments to Linux CCN examples
[nepi.git] / src / nepi / resources / planetlab / node.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 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.resource import ResourceManager, clsinit_copy, ResourceState
22 from nepi.resources.linux.node import LinuxNode
23
24 from nepi.resources.planetlab.plcapi import PLCAPIFactory 
25
26 reschedule_delay = "0.5s"
27
28 @clsinit_copy
29 class PlanetlabNode(LinuxNode):
30     _rtype = "PlanetlabNode"
31
32     @classmethod
33     def _register_attributes(cls):
34         cls._remove_attribute("username")
35
36         ip = Attribute("ip", "PlanetLab host public IP address",
37                 flags = Flags.ReadOnly)
38
39         slicename = Attribute("slice", "PlanetLab slice name",
40                 flags = Flags.Credential)
41
42         pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
43                 default = "www.planet-lab.eu",
44                 flags = Flags.Credential)
45
46         pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
47                 default = "https://%(hostname)s:443/PLCAPI/",
48                 flags = Flags.ExecReadOnly)
49
50         city = Attribute("city",
51                 "Constrain location (city) during resource discovery. May use wildcards.",
52                 flags = Flags.Filter)
53
54         country = Attribute("country",
55                 "Constrain location (country) during resource discovery. May use wildcards.",
56                 flags = Flags.Filter)
57
58         region = Attribute("region",
59                 "Constrain location (region) during resource discovery. May use wildcards.",
60                 flags = Flags.Filter)
61
62         architecture = Attribute("architecture",
63                 "Constrain architecture during resource discovery.",
64                 type = Types.Enumerate,
65                 allowed = ["x86_64",
66                             "i386"],
67                 flags = Flags.Filter)
68
69         operating_system = Attribute("operatingSystem",
70                 "Constrain operating system during resource discovery.",
71                 type = Types.Enumerate,
72                 allowed =  ["f8",
73                             "f12",
74                             "f14",
75                             "centos",
76                             "other"],
77                 flags = Flags.Filter)
78
79         site = Attribute("site",
80                 "Constrain the PlanetLab site this node should reside on.",
81                 type = Types.Enumerate,
82                 allowed = ["PLE",
83                             "PLC",
84                             "PLJ"],
85                 flags = Flags.Filter)
86
87         min_reliability = Attribute("minReliability",
88                 "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
89                 type = Types.Double,
90                 range = (1, 100),
91                 flags = Flags.Filter)
92
93         max_reliability = Attribute("maxReliability",
94                 "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
95                 type = Types.Double,
96                 range = (1, 100),
97                 flags = Flags.Filter)
98
99         min_bandwidth = Attribute("minBandwidth",
100                 "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
101                 type = Types.Double,
102                 range = (0, 2**31),
103                 flags = Flags.Filter)
104
105         max_bandwidth = Attribute("maxBandwidth",
106                 "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
107                 type = Types.Double,
108                 range = (0, 2**31),
109                 flags = Flags.Filter)
110
111         min_load = Attribute("minLoad",
112                 "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
113                 type = Types.Double,
114                 range = (0, 2**31),
115                 flags = Flags.Filter)
116
117         max_load = Attribute("maxLoad",
118                 "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
119                 type = Types.Double,
120                 range = (0, 2**31),
121                 flags = Flags.Filter)
122
123         min_cpu = Attribute("minCpu",
124                 "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
125                 type = Types.Double,
126                 range = (0, 100),
127                 flags = Flags.Filter)
128
129         max_cpu = Attribute("maxCpu",
130                 "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
131                 type = Types.Double,
132                 range = (0, 100),
133                 flags = Flags.Filter)
134
135         timeframe = Attribute("timeframe",
136                 "Past time period in which to check information about the node. Values are year,month, week, latest",
137                 default = "week",
138                 type = Types.Enumerate,
139                 allowed = ["latest",
140                             "week",
141                             "month",
142                             "year"],
143                  flags = Flags.Filter)
144
145         cls._register_attribute(ip)
146         cls._register_attribute(slicename)
147         cls._register_attribute(pl_url)
148         cls._register_attribute(pl_ptn)
149         cls._register_attribute(city)
150         cls._register_attribute(country)
151         cls._register_attribute(region)
152         cls._register_attribute(architecture)
153         cls._register_attribute(operating_system)
154         cls._register_attribute(min_reliability)
155         cls._register_attribute(max_reliability)
156         cls._register_attribute(min_bandwidth)
157         cls._register_attribute(max_bandwidth)
158         cls._register_attribute(min_load)
159         cls._register_attribute(max_load)
160         cls._register_attribute(min_cpu)
161         cls._register_attribute(max_cpu)
162         cls._register_attribute(timeframe)
163
164     def __init__(self, ec, guid):
165         super(PLanetlabNode, self).__init__(ec, guid)
166
167         self._plapi = None
168     
169     @property
170     def plapi(self):
171         if not self._plapi:
172             slicename = self.get("slice")
173             pl_pass = self.get("password")
174             pl_url = self.get("plcApiUrl")
175             pl_ptn = self.get("plcApiPattern")
176
177             self._plapi =  PLCAPIFactory.get_api(slicename, pl_pass, pl_url,
178                     pl_ptn)
179             
180         return self._plapi
181
182     @property
183     def os(self):
184         if self._os:
185             return self._os
186
187         if (not self.get("hostname") or not self.get("username")):
188             msg = "Can't resolve OS, insufficient data "
189             self.error(msg)
190             raise RuntimeError, msg
191
192         (out, err), proc = self.execute("cat /etc/issue", with_lock = True)
193
194         if err and proc.poll():
195             msg = "Error detecting OS "
196             self.error(msg, out, err)
197             raise RuntimeError, "%s - %s - %s" %( msg, out, err )
198
199         if out.find("Fedora release 12") == 0:
200             self._os = "f12"
201         elif out.find("Fedora release 14") == 0:
202             self._os = "f14"
203         else:
204             msg = "Unsupported OS"
205             self.error(msg, out)
206             raise RuntimeError, "%s - %s " %( msg, out )
207
208         return self._os
209
210     def provision(self):
211         if not self.is_alive():
212             self._state = ResourceState.FAILED
213             msg = "Deploy failed. Unresponsive node %s" % self.get("hostname")
214             self.error(msg)
215             raise RuntimeError, msg
216
217         if self.get("cleanProcesses"):
218             self.clean_processes()
219
220         if self.get("cleanHome"):
221             self.clean_home()
222        
223         self.mkdir(self.node_home)
224
225         super(PlanetlabNode, self).provision()
226
227     def deploy(self):
228         if self.state == ResourceState.NEW:
229             try:
230                self.discover()
231                if self.state == ResourceState.DISCOVERED:
232                    self.provision()
233             except:
234                 self._state = ResourceState.FAILED
235                 raise
236
237         if self.state != ResourceState.PROVISIONED:
238            self.ec.schedule(reschedule_delay, self.deploy)
239
240         super(PlanetlabNode, self).deploy()
241
242     def valid_connection(self, guid):
243         # TODO: Validate!
244         return True
245
246     def clean_processes(self, killer = False):
247         self.info("Cleaning up processes")
248     
249         # Hardcore kill
250         cmd = ("sudo -S killall python tcpdump || /bin/true ; " +
251             "sudo -S killall python tcpdump || /bin/true ; " +
252             "sudo -S kill $(ps -N -T -o pid --no-heading | grep -v $PPID | sort) || /bin/true ; " +
253             "sudo -S killall -u root || /bin/true ; " +
254             "sudo -S killall -u root || /bin/true ; ")
255
256         out = err = ""
257         (out, err), proc = self.execute(cmd, retry = 1, with_lock = True) 
258             
259     def blacklist(self):
260         # TODO!!!!
261         self.warn(" Blacklisting malfunctioning node ")
262         #import util
263         #util.appendBlacklist(self.hostname)
264