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