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, \
23 from nepi.resources.linux.node import LinuxNode
24 from nepi.resources.planetlab.plcapi import PLCAPIFactory
25 from nepi.util.timefuncs import tnow, tdiff, tdiffsec, stabsformat
29 class PlanetlabNode(LinuxNode):
30 _rtype = "PlanetlabNode"
33 def _register_attributes(cls):
34 ip = Attribute("ip", "PlanetLab host public IP address",
35 flags = Flags.ReadOnly)
37 pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
38 default = "www.planet-lab.eu",
39 flags = Flags.Credential)
41 pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
42 default = "https://%(hostname)s:443/PLCAPI/",
43 flags = Flags.ExecReadOnly)
45 pl_user = Attribute("pluser", "PlanetLab account user, as the one to authenticate in the website) ",
46 flags = Flags.Credential)
48 pl_password = Attribute("password", "PlanetLab account password, as the one to authenticate in the website) ",
49 flags = Flags.Credential)
51 city = Attribute("city",
52 "Constrain location (city) during resource discovery. May use wildcards.",
55 country = Attribute("country",
56 "Constrain location (country) during resource discovery. May use wildcards.",
59 region = Attribute("region",
60 "Constrain location (region) during resource discovery. May use wildcards.",
63 architecture = Attribute("architecture",
64 "Constrain architecture during resource discovery.",
65 type = Types.Enumerate,
70 operating_system = Attribute("operatingSystem",
71 "Constrain operating system during resource discovery.",
72 type = Types.Enumerate,
80 site = Attribute("site",
81 "Constrain the PlanetLab site this node should reside on.",
82 type = Types.Enumerate,
88 min_reliability = Attribute("minReliability",
89 "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
94 max_reliability = Attribute("maxReliability",
95 "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
100 min_bandwidth = Attribute("minBandwidth",
101 "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
104 flags = Flags.Filter)
106 max_bandwidth = Attribute("maxBandwidth",
107 "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
110 flags = Flags.Filter)
112 min_load = Attribute("minLoad",
113 "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
116 flags = Flags.Filter)
118 max_load = Attribute("maxLoad",
119 "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
122 flags = Flags.Filter)
124 min_cpu = Attribute("minCpu",
125 "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
128 flags = Flags.Filter)
130 max_cpu = Attribute("maxCpu",
131 "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
134 flags = Flags.Filter)
136 timeframe = Attribute("timeframe",
137 "Past time period in which to check information about the node. Values are year,month, week, latest",
139 type = Types.Enumerate,
144 flags = Flags.Filter)
146 cls._register_attribute(ip)
147 cls._register_attribute(pl_url)
148 cls._register_attribute(pl_ptn)
149 cls._register_attribute(pl_user)
150 cls._register_attribute(pl_password)
151 cls._register_attribute(site)
152 cls._register_attribute(city)
153 cls._register_attribute(country)
154 cls._register_attribute(region)
155 cls._register_attribute(architecture)
156 cls._register_attribute(operating_system)
157 cls._register_attribute(min_reliability)
158 cls._register_attribute(max_reliability)
159 cls._register_attribute(min_bandwidth)
160 cls._register_attribute(max_bandwidth)
161 cls._register_attribute(min_load)
162 cls._register_attribute(max_load)
163 cls._register_attribute(min_cpu)
164 cls._register_attribute(max_cpu)
165 cls._register_attribute(timeframe)
167 def __init__(self, ec, guid):
168 super(PlanetlabNode, self).__init__(ec, guid)
175 pl_user = self.get("pluser")
176 pl_pass = self.get("password")
177 pl_url = self.get("plcApiUrl")
178 pl_ptn = self.get("plcApiPattern")
180 self._plapi = PLCAPIFactory.get_api(pl_user, pl_pass, pl_url,
186 # hostname = self.get("hostname")
188 # node_id = self.check_alive_and_active(hostname=hostname)
190 # from random import choice
191 # nodes = self.filter_based_on_attributes()
192 # nodes_alive = self.check_alive_and_active(nodes)
194 # node_id = choice(nodes_alive)
197 # self._discover_time = tnow()
198 # self._state = ResourceState.DISCOVERED
201 #def provision(self):
203 def filter_based_on_attributes(self):
204 # Map attributes with tagnames of PL
205 timeframe = self.get("timeframe")[0]
208 'country' : 'country',
210 'architecture' : 'arch',
211 'operatingSystem' : 'fcdistro',
212 #'site' : 'pldistro',
213 'minReliability' : 'reliability%s' % timeframe,
214 'maxReliability' : 'reliability%s' % timeframe,
215 'minBandwidth' : 'bw%s' % timeframe,
216 'maxBandwidth' : 'bw%s' % timeframe,
217 'minLoad' : 'load%s' % timeframe,
218 'maxLoad' : 'load%s' % timeframe,
219 'minCpu' : 'cpu%s' % timeframe,
220 'maxCpu' : 'cpu%s' % timeframe,
225 for attr_name, attr_obj in self._attrs.iteritems():
226 attr_value = self.get(attr_name)
228 if attr_value is not None and attr_obj.flags == 8 and not 'min' in attr_name \
229 and not 'max' in attr_name and attr_name != 'timeframe':
230 attr_tag = attr_to_tags[attr_name]
231 filters['tagname'] = attr_tag
232 filters['value'] = attr_value
233 node_tags = self.plapi.get_node_tags(filters)
234 if node_tags is not None:
235 if len(nodes_id) == 0:
236 for node_tag in node_tags:
237 nodes_id.append(node_tag['node_id'])
240 for node_tag in node_tags:
241 if node_tag['node_id'] in nodes_id:
242 nodes_id_tmp.append(node_tag['node_id'])
243 if len(nodes_id_tmp):
244 nodes_id = set(nodes_id) & set(nodes_id_tmp)
249 elif attr_value is not None and attr_obj.flags == 8 and ('min' or 'max') in attr_name:
250 attr_tag = attr_to_tags[attr_name]
251 filters['tagname'] = attr_tag
252 node_tags = self.plapi.get_node_tags(filters)
253 if node_tags is not None:
254 if len(nodes_id) == 0:
255 for node_tag in node_tags:
256 if 'min' in attr_name and node_tag['value'] != 'n/a' and \
257 float(node_tag['value']) > attr_value:
258 nodes_id.append(node_tag['node_id'])
259 elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
260 float(node_tag['value']) < attr_value:
261 nodes_id.append(node_tag['node_id'])
264 for node_tag in node_tags:
265 if 'min' in attr_name and node_tag['value'] != 'n/a' and \
266 float(node_tag['value']) > attr_value and \
267 node_tag['node_id'] in nodes_id:
268 nodes_id_tmp.append(node_tag['node_id'])
269 elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
270 float(node_tag['value']) < attr_value and \
271 node_tag['node_id'] in nodes_id:
272 nodes_id_tmp.append(node_tag['node_id'])
273 if len(nodes_id_tmp):
274 nodes_id = set(nodes_id) & set(nodes_id_tmp)
280 def check_alive_and_active(self, nodes_id=None, hostname=None):
281 if nodes_id is None and hostname is None:
282 msg = "Specify nodes_id or hostname"
283 raise RuntimeError, msg
284 if nodes_id is not None and hostname is not None:
285 msg = "Specify either nodes_id or hostname"
286 raise RuntimeError, msg
291 filters['run_level'] = 'boot'
292 filters['boot_state'] = 'boot'
293 filters['node_type'] = 'regular'
294 filters['>last_contact'] = int(time.time()) - 2*3600
296 filters['node_id'] = list(nodes_id)
297 alive_nodes_id = self.plapi.get_nodes(filters, fields=['node_id'])
299 filters['hostname'] = hostname
300 alive_nodes_id = self.plapi.get_nodes(filters, fields=['node_id'])
301 if len(alive_nodes_id) == 0:
305 for node_id in alive_nodes_id:
306 nid = node_id['node_id']
310 # ip = self.plapi.get_interfaces({'node_id':nid}, fields=['ip'])
311 # self.set('ip', ip[0]['ip'])
312 #de hostname para que provision haga add_node_slice, check que ip coincide con hostname
316 msg = "Discovery failed. No candidates found for node"
318 raise RuntimeError, msg
321 def valid_connection(self, guid):
325 # def blacklist(self):
327 # self.warn(" Blacklisting malfunctioning node ")
329 # #util.appendBlacklist(self.hostname)