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/>.
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
28 reschedule_delay = "0.5s"
31 class PlanetlabNode(LinuxNode):
32 _rtype = "PlanetLabNode"
35 def _register_attributes(cls):
36 cls._remove_attribute("username")
38 ip = Attribute("ip", "PlanetLab host public IP address",
39 flags = Flags.ReadOnly)
41 slicename = Attribute("slice", "PlanetLab slice name",
42 flags = Flags.Credential)
44 pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
45 default = "www.planet-lab.eu",
46 flags = Flags.Credential)
48 pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
49 default = "https://%(hostname)s:443/PLCAPI/",
50 flags = Flags.ExecReadOnly)
52 city = Attribute("city",
53 "Constrain location (city) during resource discovery. May use wildcards.",
56 country = Attribute("country",
57 "Constrain location (country) during resource discovery. May use wildcards.",
60 region = Attribute("region",
61 "Constrain location (region) during resource discovery. May use wildcards.",
64 architecture = Attribute("architecture",
65 "Constrain architecture during resource discovery.",
66 type = Types.Enumerate,
71 operating_system = Attribute("operatingSystem",
72 "Constrain operating system during resource discovery.",
73 type = Types.Enumerate,
81 site = Attribute("site",
82 "Constrain the PlanetLab site this node should reside on.",
83 type = Types.Enumerate,
89 min_reliability = Attribute("minReliability",
90 "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
95 max_reliability = Attribute("maxReliability",
96 "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
101 min_bandwidth = Attribute("minBandwidth",
102 "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
105 flags = Flags.Filter)
107 max_bandwidth = Attribute("maxBandwidth",
108 "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
111 flags = Flags.Filter)
113 min_load = Attribute("minLoad",
114 "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
117 flags = Flags.Filter)
119 max_load = Attribute("maxLoad",
120 "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
123 flags = Flags.Filter)
125 min_cpu = Attribute("minCpu",
126 "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
129 flags = Flags.Filter)
131 max_cpu = Attribute("maxCpu",
132 "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
135 flags = Flags.Filter)
137 timeframe = Attribute("timeframe",
138 "Past time period in which to check information about the node. Values are year,month, week, latest",
140 type = Types.Enumerate,
145 flags = Flags.Filter)
147 cls._register_attribute(ip)
148 cls._register_attribute(slicename)
149 cls._register_attribute(pl_url)
150 cls._register_attribute(pl_ptn)
151 cls._register_attribute(city)
152 cls._register_attribute(country)
153 cls._register_attribute(region)
154 cls._register_attribute(architecture)
155 cls._register_attribute(operating_system)
156 cls._register_attribute(min_reliability)
157 cls._register_attribute(max_reliability)
158 cls._register_attribute(min_bandwidth)
159 cls._register_attribute(max_bandwidth)
160 cls._register_attribute(min_load)
161 cls._register_attribute(max_load)
162 cls._register_attribute(min_cpu)
163 cls._register_attribute(max_cpu)
164 cls._register_attribute(timeframe)
166 def __init__(self, ec, guid):
167 super(PLanetLabNode, self).__init__(ec, guid)
171 self._logger = logging.getLogger("PlanetLabNode")
176 slicename = self.get("slice")
177 pl_pass = self.get("password")
178 pl_url = self.get("plcApiUrl")
179 pl_ptn = self.get("plcApiPattern")
181 self._plapi = PLCAPIFactory.get_api(slicename, pl_pass, pl_url,
191 if (not self.get("hostname") or not self.get("username")):
192 msg = "Can't resolve OS, insufficient data "
194 raise RuntimeError, msg
196 (out, err), proc = self.execute("cat /etc/issue", with_lock = True)
198 if err and proc.poll():
199 msg = "Error detecting OS "
200 self.error(msg, out, err)
201 raise RuntimeError, "%s - %s - %s" %( msg, out, err )
203 if out.find("Fedora release 12") == 0:
205 elif out.find("Fedora release 14") == 0:
208 msg = "Unsupported OS"
210 raise RuntimeError, "%s - %s " %( msg, out )
219 # Get the list of nodes that match the filters
223 if not self.is_alive():
224 self._state = ResourceState.FAILED
225 msg = "Deploy failed. Unresponsive node %s" % self.get("hostname")
227 raise RuntimeError, msg
229 if self.get("cleanProcesses"):
230 self.clean_processes()
232 if self.get("cleanHome"):
235 self.mkdir(self.node_home)
237 super(PlanetlabNode, self).discover()
240 if not self.is_alive():
241 self._state = ResourceState.FAILED
242 msg = "Deploy failed. Unresponsive node %s" % self.get("hostname")
244 raise RuntimeError, msg
246 if self.get("cleanProcesses"):
247 self.clean_processes()
249 if self.get("cleanHome"):
252 self.mkdir(self.node_home)
254 super(PlanetlabNode, self).provision()
257 if self.state == ResourceState.NEW:
260 if self.state == ResourceState.DISCOVERED:
263 self._state = ResourceState.FAILED
266 if self.state != ResourceState.PROVISIONED:
267 self.ec.schedule(reschedule_delay, self.deploy)
269 super(PlanetlabNode, self).deploy()
271 def valid_connection(self, guid):
275 def clean_processes(self, killer = False):
276 self.info("Cleaning up processes")
279 cmd = ("sudo -S killall python tcpdump || /bin/true ; " +
280 "sudo -S killall python tcpdump || /bin/true ; " +
281 "sudo -S kill $(ps -N -T -o pid --no-heading | grep -v $PPID | sort) || /bin/true ; " +
282 "sudo -S killall -u root || /bin/true ; " +
283 "sudo -S killall -u root || /bin/true ; ")
286 (out, err), proc = self.execute(cmd, retry = 1, with_lock = True)
294 # TODO: FIX NOT ALIVE!!!!
295 (out, err), proc = self.execute("echo 'ALIVE' || (echo 'NOTALIVE') >&2", retry = 5,
299 trace = traceback.format_exc()
300 msg = "Unresponsive host %s " % err
301 self.error(msg, out, trace)
304 if out.strip().startswith('ALIVE'):
307 msg = "Unresponsive host "
308 self.error(msg, out, err)
313 self.warn(" Blacklisting malfunctioning node ")
315 #util.appendBlacklist(self.hostname)