Update PlanetLab node, adding function to filter by attributes
[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         ip = Attribute("ip", "PlanetLab host public IP address",
34                 flags = Flags.ReadOnly)
35
36         pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
37                 default = "www.planet-lab.eu",
38                 flags = Flags.Credential)
39
40         pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
41                 default = "https://%(hostname)s:443/PLCAPI/",
42                 flags = Flags.ExecReadOnly)
43     
44         pl_user = Attribute("pluser", "PlanetLab account user, as the one to authenticate in the website) ",
45                 flags = Flags.Credential)
46
47         pl_password = Attribute("password", "PlanetLab account password, as the one to authenticate in the website) ",
48                 flags = Flags.Credential)
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(pl_url)
147         cls._register_attribute(pl_ptn)
148         cls._register_attribute(pl_user)
149         cls._register_attribute(pl_password)
150         cls._register_attribute(site)
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)
165
166     def __init__(self, ec, guid):
167         super(PlanetlabNode, self).__init__(ec, guid)
168
169         self._plapi = None
170     
171     @property
172     def plapi(self):
173         if not self._plapi:
174             pl_user = self.get("pluser")
175             pl_pass = self.get("password")
176             pl_url = self.get("plcApiUrl")
177             pl_ptn = self.get("plcApiPattern")
178
179             self._plapi =  PLCAPIFactory.get_api(pl_user, pl_pass, pl_url,
180                     pl_ptn)
181             
182         return self._plapi
183
184     #def discover(self):
185         #if self.get("hostname") or self.get("ip"):
186             #pass
187             #return node_id de hostname para que provision haga add_node_slice, check que ip coincide con hostname
188
189     def filter_based_on_attributes(self):
190         # Map attributes with tagnames of PL
191         timeframe = self.get("timeframe")[0]
192         attr_to_tags = {
193             'city' : 'city',
194             'country' : 'country',
195             'region' : 'region',
196             'architecture' : 'arch',
197             'operatingSystem' : 'fcdistro',
198             #'site' : 'pldistro',
199             'minReliability' : 'reliability%s' % timeframe,
200             'maxReliability' : 'reliability%s' % timeframe,
201             'minBandwidth' : 'bw%s' % timeframe,
202             'maxBandwidth' : 'bw%s' % timeframe,
203             'minLoad' : 'load%s' % timeframe,
204             'maxLoad' : 'load%s' % timeframe,
205             'minCpu' : 'cpu%s' % timeframe,
206             'maxCpu' : 'cpu%s' % timeframe,
207         }
208         
209         nodes_id = []
210         filters = {}
211         for attr_name, attr_obj in self._attrs.iteritems():
212             attr_value = self.get(attr_name)
213             print nodes_id
214             if attr_value is not None and attr_obj.flags == 8 and not 'min' in attr_name \
215                 and not 'max'in attr_name and attr_name != 'timeframe':
216                 attr_tag = attr_to_tags[attr_name]
217                 filters['tagname'] = attr_tag
218                 filters['value'] = attr_value
219                 node_tags = self.plapi.get_node_tags(filters)
220                 if node_tags is not None:
221                     if len(nodes_id) == 0:
222                         for node_tag in node_tags:
223                             nodes_id.append(node_tag['node_id'])
224                     else:
225                         nodes_id_tmp = []
226                         for node_tag in node_tags:
227                             if node_tag['node_id'] in nodes_id:
228                                 nodes_id_tmp.append(node_tag['node_id'])
229                         if len(nodes_id_tmp):
230                             nodes_id = set(nodes_id) & set(nodes_id_tmp)
231                         else:
232                             return "No node matching your filters 1"
233                 else:
234                     return "No node matching your filters 2"
235             elif attr_value is not None and attr_obj.flags == 8 and ('min' or 'max') in attr_name:
236                 attr_tag = attr_to_tags[attr_name]
237                 filters['tagname'] = attr_tag
238                 node_tags = self.plapi.get_node_tags(filters)
239                 if node_tags is not None:
240                     if len(nodes_id) == 0:
241                         for node_tag in node_tags:
242                             if 'min' in attr_name and node_tag['value'] != 'n/a' and \
243                                 float(node_tag['value']) > attr_value:
244                                 nodes_id.append(node_tag['node_id'])
245                             elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
246                                 float(node_tag['value']) < attr_value:
247                                 nodes_id.append(node_tag['node_id'])
248                     else:
249                         nodes_id_tmp = []
250                         for node_tag in node_tags:
251                             if 'min' in attr_name and node_tag['value'] != 'n/a' and \
252                                 float(node_tag['value']) > attr_value and \
253                                 node_tag['node_id'] in nodes_id:
254                                 nodes_id_tmp.append(node_tag['node_id'])
255                             elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
256                                 float(node_tag['value']) < attr_value and \
257                                 node_tag['node_id'] in nodes_id:
258                                 nodes_id_tmp.append(node_tag['node_id'])
259                         if len(nodes_id_tmp):
260                             nodes_id = set(nodes_id) & set(nodes_id_tmp)
261                         else:
262                             return "No node matching your filters 3"
263
264         return nodes_id
265                     
266     #def check_alive_and_active(self, nodes_id):
267            
268                         
269
270
271
272     def valid_connection(self, guid):
273         # TODO: Validate!
274         return True
275
276 #    def blacklist(self):
277 #        # TODO!!!!
278 #        self.warn(" Blacklisting malfunctioning node ")
279 #        #import util
280 #        #util.appendBlacklist(self.hostname)
281