2 # NEPI, a framework to manage network experiments
3 # Copyright (C) 2013 INRIA
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.
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.
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/>.
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
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
24 from nepi.resources.planetlab.plcapi import PLCAPIFactory
26 reschedule_delay = "0.5s"
29 class PlanetlabNode(LinuxNode):
30 _rtype = "PlanetlabNode"
33 def _register_attributes(cls):
34 cls._remove_attribute("username")
36 ip = Attribute("ip", "PlanetLab host public IP address",
37 flags = Flags.ReadOnly)
39 slicename = Attribute("slice", "PlanetLab slice name",
40 flags = Flags.Credential)
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)
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)
50 city = Attribute("city",
51 "Constrain location (city) during resource discovery. May use wildcards.",
54 country = Attribute("country",
55 "Constrain location (country) during resource discovery. May use wildcards.",
58 region = Attribute("region",
59 "Constrain location (region) during resource discovery. May use wildcards.",
62 architecture = Attribute("architecture",
63 "Constrain architecture during resource discovery.",
64 type = Types.Enumerate,
69 operating_system = Attribute("operatingSystem",
70 "Constrain operating system during resource discovery.",
71 type = Types.Enumerate,
79 site = Attribute("site",
80 "Constrain the PlanetLab site this node should reside on.",
81 type = Types.Enumerate,
87 min_reliability = Attribute("minReliability",
88 "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
93 max_reliability = Attribute("maxReliability",
94 "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
99 min_bandwidth = Attribute("minBandwidth",
100 "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
103 flags = Flags.Filter)
105 max_bandwidth = Attribute("maxBandwidth",
106 "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
109 flags = Flags.Filter)
111 min_load = Attribute("minLoad",
112 "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
115 flags = Flags.Filter)
117 max_load = Attribute("maxLoad",
118 "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
121 flags = Flags.Filter)
123 min_cpu = Attribute("minCpu",
124 "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
127 flags = Flags.Filter)
129 max_cpu = Attribute("maxCpu",
130 "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
133 flags = Flags.Filter)
135 timeframe = Attribute("timeframe",
136 "Past time period in which to check information about the node. Values are year,month, week, latest",
138 type = Types.Enumerate,
143 flags = Flags.Filter)
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)
164 def __init__(self, ec, guid):
165 super(PLanetlabNode, self).__init__(ec, guid)
172 slicename = self.get("slice")
173 pl_pass = self.get("password")
174 pl_url = self.get("plcApiUrl")
175 pl_ptn = self.get("plcApiPattern")
177 self._plapi = PLCAPIFactory.get_api(slicename, pl_pass, pl_url,
187 if (not self.get("hostname") or not self.get("username")):
188 msg = "Can't resolve OS, insufficient data "
190 raise RuntimeError, msg
192 (out, err), proc = self.execute("cat /etc/issue", with_lock = True)
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 )
199 if out.find("Fedora release 12") == 0:
201 elif out.find("Fedora release 14") == 0:
204 msg = "Unsupported OS"
206 raise RuntimeError, "%s - %s " %( msg, out )
211 if not self.is_alive():
212 self._state = ResourceState.FAILED
213 msg = "Deploy failed. Unresponsive node %s" % self.get("hostname")
215 raise RuntimeError, msg
217 if self.get("cleanProcesses"):
218 self.clean_processes()
220 if self.get("cleanHome"):
223 self.mkdir(self.node_home)
225 super(PlanetlabNode, self).provision()
228 if self.state == ResourceState.NEW:
231 if self.state == ResourceState.DISCOVERED:
234 self._state = ResourceState.FAILED
237 if self.state != ResourceState.PROVISIONED:
238 self.ec.schedule(reschedule_delay, self.deploy)
240 super(PlanetlabNode, self).deploy()
242 def valid_connection(self, guid):
246 def clean_processes(self, killer = False):
247 self.info("Cleaning up processes")
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 ; ")
257 (out, err), proc = self.execute(cmd, retry = 1, with_lock = True)
261 self.warn(" Blacklisting malfunctioning node ")
263 #util.appendBlacklist(self.hostname)