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 version 2 as
7 # published by the Free Software Foundation;
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 # Author: Lucia Guevgeozian Odizzio <lucia.guevgeozian_odizzio@inria.fr>
19 from __future__ import print_function
27 class MANIFOLDAPI(object):
29 API to query different data platforms as SFA, TopHat, OML Central Server,
30 using Manifold Framework, the backend of MySlice.
32 def __init__(self, username, password, hostname, urlpattern):
34 self.auth_pwd = dict(AuthMethod='password', Username=username,
36 self._url = urlpattern % {'hostname':hostname}
37 self.lock = threading.Lock()
38 self.auth = self.get_session_key()
42 return xmlrpc.client.Server(self._url, allow_none = True)
44 def get_session_key(self):
46 Retrieves the session key, in order to use the same session for
49 query = {'timestamp' : 'now', 'object': 'local:session',
50 'filters' : [], 'fields' : [], 'action' : 'create'}
52 session = self.api.forward(self.auth_pwd, query)
54 if not session['value']:
55 msg = "Can not authenticate in Manifold API"
56 raise RuntimeError(msg)
58 session_key = session['value'][0]['session']
59 return dict(AuthMethod='session', session=session_key)
61 def get_resource_info(self, filters=None, fields=None):
63 Create and execute the Manifold API Query to get the resources
64 according fields and filters.
65 :param filters: resource's constraints for the experiment
67 :param fields: desire fields in the result of the query
70 query = {'action' : 'get', 'object' : 'resource'}
73 filters = self._map_attr_to_resource_filters(filters)
76 for filtername, filtervalue in filters.items():
77 newfilter = [filtername, "==", filtervalue]
78 qfilters.append(newfilter)
80 query['filters'] = qfilters
83 fields = self._check_valid_fields(fields)
86 query['fields'] = fields
88 return self.api.forward(self.auth, query)['value']
90 def get_resource_urn(self, filters=None):
92 Retrieves the resources urn of the resources matching filters.
94 return self.get_resource_info(filters, 'urn')
96 def get_slice_resources(self, slicename):
98 Retrieves resources attached to user's slice.
99 return value: list of resources' urn
102 query = {'action' : 'get', 'object' : 'resource',
103 'filters' : [['slice','==', slicename]],
107 value = self.api.forward(self.auth, query)['value']
109 for resource in value:
110 result.append(resource['urn'])
114 def add_resource_to_slice(self, slicename, resource_urn):
116 Add resource to user's slice. The query needs to specify the new
117 resource plus the previous resources already in the slice.
119 resources = self.get_slice_resources(slicename)
120 resources.append(resource_urn)
126 urn_list.append(urn_dict)
128 query = {'action' : 'update', 'object' : 'slice',
129 'filters' : [['slice_hrn','==', slicename]],
130 'params' : {'resource' : urn_list}}
133 self.api.forward(self.auth, query)
135 resources = self.get_slice_resources(slicename)
136 if resource_urn in resources:
139 msg = "Failed while trying to add %s to slice" % resource_urn
141 # check how to do warning
144 def remove_resource_from_slice(self, slicename, resource_urn):
146 Remove resource from user's slice. The query needs to specify the list
147 of previous resources in the slice without the one to be remove.
149 resources = self.get_slice_resources(slicename)
150 resources.remove(resource_urn)
156 urn_list.append(urn_dict)
158 query = {'action' : 'update', 'object' : 'slice',
159 'filters' : [['slice_hrn','==', slicename]],
160 'params' : {'resource' : urn_list}}
163 self.api.forward(self.auth, query)
165 resources = self.get_slice_resources(slicename)
166 if resource_urn not in resources:
169 msg = "Failed while trying to remove %s to slice" % resource_urn
170 # check how to do warning
173 def _get_metadata(self):
175 This method is useful to retrive metadata from different platforms
176 in order to update fields and possible filters.
178 query = {'action' : 'get', 'object' : 'local:object',
179 'filters' : [['table','=','resource']]}
181 res = self.api.forward(self.auth, query)
183 valid_fields = list()
184 for i in res['value'][0]['column']:
185 valid_fields.append(i['name'])
189 def _map_attr_to_resource_filters(self, filters):
191 Depending on the object used for the Manifold query, the filters and
192 fields can change its sintaxis. A resource field in a slice object
193 query adds 'resource.' to the field. Other changes don't follow any
194 particular convention.
196 #TODO: find out useful filters
198 'hostname' : 'hostname',
199 'longitude' : 'longitude',
200 'latitude' : 'latitude',
201 'network' : 'network',
202 'component_id' : 'component_id'
205 mapped_filters = dict()
206 for filtername, filtervalue in filters.items():
207 if attr_to_filter[filtername]:
208 new_filtername = attr_to_filter[filtername]
209 mapped_filters[new_filtername] = filtervalue
211 return mapped_filters
213 def _check_valid_fields(self, fields):
215 The fields can be a predefine set, define in the Manifold metadata.
217 valid_fields = self._get_metadata()
219 if not isinstance(fields, list):
223 if field not in valid_fields:
225 #self.warning(" Invalid Manifold field or filter ")
230 class MANIFOLDAPIFactory(object):
232 API Factory to manage a map of MANIFOLDAPI instances as key-value pairs, it
233 instanciate a single instance per key. The key represents the same SFA,
234 MF (ManiFold) credentials.
237 _lock = threading.Lock()
241 def get_api(cls, username, password,
242 #hostname = "manifold.pl.sophia.inria.fr",
243 hostname ="test.myslice.info",
244 urlpattern = "http://%(hostname)s:7080"):
246 :param username: Manifold user (also used for MySlice web login)
248 :param password: Manifold password (also used for MySlice web login)
250 :param hostname: Hostname of the Manifold API to query SFA, TopHat, etc
252 :param urlpattern: Url of the Manifold API to query SFA, TopHat, etc
253 :type urlpattern: str
256 if username and password:
257 key = cls.make_key(username, password)
259 api = cls._apis.get(key)
262 api = MANIFOLDAPI(username, password, hostname, urlpattern)
270 def make_key(cls, *args):
271 skey = "".join(map(str, args))
272 return hashlib.md5(skey).hexdigest()