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: Lucia Guevgeozian Odizzio <lucia.guevgeozian_odizzio@inria.fr>
25 class MANIFOLDAPI(object):
27 API to query different data platforms as SFA, TopHat, OML Central Server,
28 using Manifold Framework, the backend of MySlice.
30 def __init__(self, username, password, hostname, urlpattern):
32 self.auth_pwd = dict(AuthMethod='password', Username=username,
34 self._url = urlpattern % {'hostname':hostname}
35 self.lock = threading.Lock()
36 self.auth = self.get_session_key()
40 return xmlrpclib.Server(self._url, allow_none = True)
42 def get_session_key(self):
44 Retrieves the session key, in order to use the same session for
47 query = {'timestamp' : 'now', 'object': 'local:session',
48 'filters' : [], 'fields' : [], 'action' : 'create'}
50 session = self.api.forward(self.auth_pwd, query)
52 if not session['value']:
53 msg = "Can not authenticate in Manifold API"
54 raise RuntimeError, msg
56 session_key = session['value'][0]['session']
57 return dict(AuthMethod='session', session=session_key)
59 def get_resource_info(self, filters=None, fields=None):
61 Create and execute the Manifold API Query to get the resources
62 according fields and filters.
63 :param filters: resource's constraints for the experiment
65 :param fields: desire fields in the result of the query
68 query = {'action' : 'get', 'object' : 'resource'}
71 filters = self._map_attr_to_resource_filters(filters)
74 for filtername, filtervalue in filters.iteritems():
75 newfilter = [filtername, "==", filtervalue]
76 qfilters.append(newfilter)
78 query['filters'] = qfilters
81 fields = self._check_valid_fields(fields)
84 query['fields'] = fields
86 return self.api.forward(self.auth, query)['value']
88 def get_resource_urn(self, filters=None):
90 Retrieves the resources urn of the resources matching filters.
92 return self.get_resource_info(filters, 'urn')
94 def get_slice_resources(self, slicename):
96 Retrieves resources attached to user's slice.
97 return value: list of resources' urn
100 query = {'action' : 'get', 'object' : 'resource',
101 'filters' : [['slice','==', slicename]],
105 value = self.api.forward(self.auth, query)['value']
107 for resource in value:
108 result.append(resource['urn'])
112 def add_resource_to_slice(self, slicename, resource_urn):
114 Add resource to user's slice. The query needs to specify the new
115 resource plus the previous resources already in the slice.
117 resources = self.get_slice_resources(slicename)
118 resources.append(resource_urn)
124 urn_list.append(urn_dict)
126 query = {'action' : 'update', 'object' : 'slice',
127 'filters' : [['slice_hrn','==', slicename]],
128 'params' : {'resource' : urn_list}}
131 self.api.forward(self.auth, query)
133 resources = self.get_slice_resources(slicename)
134 if resource_urn in resources:
137 msg = "Failed while trying to add %s to slice" % resource_urn
139 # check how to do warning
142 def remove_resource_from_slice(self, slicename, resource_urn):
144 Remove resource from user's slice. The query needs to specify the list
145 of previous resources in the slice without the one to be remove.
147 resources = self.get_slice_resources(slicename)
148 resources.remove(resource_urn)
154 urn_list.append(urn_dict)
156 query = {'action' : 'update', 'object' : 'slice',
157 'filters' : [['slice_hrn','==', slicename]],
158 'params' : {'resource' : urn_list}}
161 self.api.forward(self.auth, query)
163 resources = self.get_slice_resources(slicename)
164 if resource_urn not in resources:
167 msg = "Failed while trying to remove %s to slice" % resource_urn
168 # check how to do warning
171 def _get_metadata(self):
173 This method is useful to retrive metadata from different platforms
174 in order to update fields and possible filters.
176 query = {'action' : 'get', 'object' : 'local:object',
177 'filters' : [['table','=','resource']]}
179 res = self.api.forward(self.auth, query)
181 valid_fields = list()
182 for i in res['value'][0]['column']:
183 valid_fields.append(i['name'])
187 def _map_attr_to_resource_filters(self, filters):
189 Depending on the object used for the Manifold query, the filters and
190 fields can change its sintaxis. A resource field in a slice object
191 query adds 'resource.' to the field. Other changes don't follow any
192 particular convention.
194 #TODO: find out useful filters
196 'hostname' : 'hostname',
197 'longitude' : 'longitude',
198 'latitude' : 'latitude',
199 'network' : 'network',
200 'component_id' : 'component_id'
203 mapped_filters = dict()
204 for filtername, filtervalue in filters.iteritems():
205 if attr_to_filter[filtername]:
206 new_filtername = attr_to_filter[filtername]
207 mapped_filters[new_filtername] = filtervalue
209 return mapped_filters
211 def _check_valid_fields(self, fields):
213 The fields can be a predefine set, define in the Manifold metadata.
215 valid_fields = self._get_metadata()
217 if not isinstance(fields, list):
221 if field not in valid_fields:
223 #self.warning(" Invalid Manifold field or filter ")
228 class MANIFOLDAPIFactory(object):
230 API Factory to manage a map of MANIFOLDAPI instances as key-value pairs, it
231 instanciate a single instance per key. The key represents the same SFA,
232 MF (ManiFold) credentials.
235 _lock = threading.Lock()
239 def get_api(cls, username, password,
240 #hostname = "manifold.pl.sophia.inria.fr",
241 hostname ="test.myslice.info",
242 urlpattern = "http://%(hostname)s:7080"):
244 :param username: Manifold user (also used for MySlice web login)
246 :param password: Manifold password (also used for MySlice web login)
248 :param hostname: Hostname of the Manifold API to query SFA, TopHat, etc
250 :param urlpattern: Url of the Manifold API to query SFA, TopHat, etc
251 :type urlpattern: str
254 if username and password:
255 key = cls.make_key(username, password)
257 api = cls._apis.get(key)
260 api = MANIFOLDAPI(username, password, hostname, urlpattern)
268 def make_key(cls, *args):
269 skey = "".join(map(str, args))
270 return hashlib.md5(skey).hexdigest()