Merge branch 'onelab' of ssh://git.onelab.eu/git/myslice into onelab
[myslice.git] / sla / slaclient / restclient.py
1 # -*- coding: utf-8 -*-
2
3 import requests
4
5 from requests.auth import HTTPBasicAuth
6
7 import xmlconverter
8 import wsag_model
9
10 from django.conf import settings
11
12
13 """REST client to SLA Manager.
14
15 Contains a generic rest client and wrappers over this generic client 
16 for each resource.
17
18 Each resource client implements business-like() functions, but
19 returns a tuple (output, requests.Response)
20
21 The resource clients are initialized with the rooturl and a path, which 
22 are combined to build the resource url. The path is defaulted to the known
23 resource path. So, for example, to create a agreements client:
24
25 c = Agreements("http://localhost/slagui-service")
26
27 A Factory facility is provided to create resource client instances. The
28 Factory uses "rooturl" module variable to use as rooturl parameter.
29
30 restclient.rooturl = "http://localhost/slagui-service"
31 c = restclient.Factory.agreements()
32
33 """
34
35 _PROVIDERS_PATH = "providerso"
36 _AGREEMENTS_PATH = "agreementso"
37 _TEMPLATES_PATH = "templateso"
38 _VIOLATIONS_PATH = "violationso"
39 _ENFORCEMENTJOBS_PATH = "enforcements"
40
41 rooturl = settings.SLA_MANAGER_URL
42
43 # SLA_MANAGER_USER = "normal_user"
44 # SLA_MANAGER_PASSWORD = "password"
45
46 class Factory(object):
47     @staticmethod
48     def agreements():
49         """Returns aREST client for Agreements
50
51         :rtype : Agreements
52          """
53         return Agreements(rooturl)
54
55     @staticmethod
56     def providers():
57         """Returns aREST client for Providers
58
59         :rtype : Providers
60         """
61         return Providers(rooturl)
62
63     @staticmethod
64     def violations():
65         """Returns aREST client for Violations
66
67         :rtype : Violations
68         """
69         return Violations(rooturl)
70
71     @staticmethod
72     def templates():
73         """Returns aREST client for Violations
74
75         :rtype : Violations
76         """
77         return Templates(rooturl)
78
79     @staticmethod
80     def enforcements():
81         """Returns aREST client for Enforcements jobs
82
83         :rtype : Enforcements
84         """
85         return Enforcements(rooturl)
86
87 class Client(object):
88
89     def __init__(self, root_url):
90
91         """Generic rest client using requests library
92
93         Each operation mimics the corresponding "requests" operation (arguments
94         and return)
95
96         :param str root_url: this url is used as prefix in all subsequent
97             requests
98         """
99         self.rooturl = root_url
100
101     def get(self, path, **kwargs):
102         """Just a wrapper over request.get, just in case.
103
104         Returns a requests.Response
105
106         :rtype : request.Response
107         :param str path: remaining path from root url; 
108             empty if desired path equal to rooturl.
109         :param kwargs: arguments to requests.get
110         
111         Example: 
112             c = Client("http://localhost:8080/service")
113             c.get("/resource", headers = { "accept": "application/json" })
114         """
115         url = _buildpath_(self.rooturl, path)
116         kwargs["auth"] = HTTPBasicAuth(settings.SLA_MANAGER_USER, settings.SLA_MANAGER_PASSWORD)
117         result = requests.get(url, **kwargs)
118         print "GET {} {} {}".format(
119             result.url, result.status_code, result.text[0:70])
120         return result
121     
122     def post(self, path, data=None, **kwargs):
123         """Just a wrapper over request.post, just in case
124
125         :rtype : request.Response
126         :param str path: remaining path from root url;
127             empty if desired path equal to rooturl.
128         :param dict[str, str] kwargs: arguments to requests.post
129
130         Example:
131             c = Client("http://localhost:8080/service")
132             c.post(
133                 '/resource',
134                 '{ "id": "1", "name": "provider-a" }',
135                 headers = {
136                     "content-type": "application/json",
137                     "accept": "application/xml"
138                 }
139             )
140         """
141         url = _buildpath_(self.rooturl, path)
142         kwargs["auth"] = HTTPBasicAuth(settings.SLA_MANAGER_USER, settings.SLA_MANAGER_PASSWORD)
143         result = requests.post(url, data, **kwargs)
144         location = result.headers["location"] \
145             if "location" in result.headers else "<null>"
146         print "POST {} {} Location: {}".format(
147             result.url, result.status_code, location)
148         return result
149
150     
151
152 class _Resource(object):
153
154     def __init__(self, url, converter):
155         """Provides some common operations over resources.
156
157         The operations return a structured representation of the resource.
158
159         :param str url: url to the resource
160         :param Converter converter: resouce xml converter
161
162         Some attributes are initialized to be used from the owner if needed:
163         * client: Client instance
164         * converter: resource xml converter
165         * listconverter: list of resources xml converter
166         """
167         self.client = Client(url)
168         self.converter = converter
169         self.listconverter = xmlconverter.ListConverter(self.converter)
170
171     @staticmethod
172     def _processresult(r, converter):
173
174         """Generic processing of the REST call.
175
176          If no errors, tries to convert the result to a destination entity.
177
178         :param r requests:
179         :param converter Converter:
180         """
181         if r.status_code == 404:
182             return None
183
184         content_type = r.headers.get('content-type', '')
185
186         print("content-type = " + content_type)
187         if content_type == 'application/json':
188             result = r.json()
189         elif content_type == 'application/xml':
190             xml = r.text
191             result = xmlconverter.convertstring(converter, xml)
192         else:
193             result = r.text
194         return result
195
196     def getall(self):
197         """Get all resources
198
199         """
200         r = self.client.get("")
201         resources = self._processresult(r, self.listconverter)
202         return resources, r
203
204     def getbyid(self, id):
205         """Get resource 'id'"""
206         r = self.client.get(id)
207         resource = _Resource._processresult(r, self.converter)
208         return resource, r
209
210     def get(self, params):
211         """Generic query over resource: GET /resource?q1=v1&q2=v2...
212
213         :param dict[str,str] params: values to pass as get parameters
214         """
215         r = self.client.get("", params=params)
216         resources = self._processresult(r, self.listconverter)
217         return resources, r
218
219     def create(self, body, **kwargs):
220         """Creates (POST method) a resource.
221
222         It should be convenient to set content-type header.
223
224         Usage:
225             resource.create(body, headers={'content-type': 'application/xml'})
226         """
227         r = self.client.post("", body, **kwargs)
228         r.raise_for_status()
229         return r
230
231
232 class Agreements(object):
233
234     def __init__(self, root_url, path=_AGREEMENTS_PATH):
235         """Business methods for Agreement resource
236         :param str root_url: url to the root of resources
237         :param str path: path to resource from root_url
238
239         The final url to the resource is root_url + "/" + path
240         """
241         resourceurl = _buildpath_(root_url, path)
242         converter = xmlconverter.AgreementConverter()
243         self.res = _Resource(resourceurl, converter)
244
245     def getall(self):
246         """
247         Get all agreements
248
249         :rtype : list[wsag_model.Agreement]
250         """
251         return self.res.getall()
252
253     def getbyid(self, agreementid):
254         """Get an agreement
255
256         :rtype : wsag_model.Agreement
257         """
258         return self.res.getbyid(agreementid)
259
260     def getbyconsumer(self, consumerid):
261         """Get a consumer's agreements
262
263         :rtype : list[wsag_model.Agreement]
264         """
265         return self.res.get(dict(consumerId=consumerid))
266
267     def getbyprovider(self, providerid):
268         """Get the agreements served by a provider
269
270         :rtype : list[wsag_model.Agreement]
271         """
272         return self.res.get(dict(providerId=providerid))
273
274     def getstatus(self, agreementid):
275         """Get guarantee status of an agreement
276
277         :param str agreementid :
278         :rtype : wsag_model.AgreementStatus
279         """
280         path = _buildpath_(agreementid, "guaranteestatus")
281         r = self.res.client.get(path, headers={'accept': 'application/json'})
282         json_obj = r.json()
283         status = wsag_model.AgreementStatus.json_decode(json_obj)
284
285         return status, r
286     
287     def create(self, agreement):
288         """Create a new agreement
289
290         :param str agreement: sla template in ws-agreement format.
291         """
292         return self.res.create(agreement)
293
294 class Templates(object):
295
296     def __init__(self, root_url, path=_TEMPLATES_PATH):
297         """Business methods for Templates resource
298         :param str root_url: url to the root of resources
299         :param str path: path to resource from root_url
300
301         The final url to the resource is root_url + "/" + path
302         """
303         resourceurl = _buildpath_(root_url, path)
304         converter = xmlconverter.AgreementConverter()
305         self.res = _Resource(resourceurl, converter)
306
307     def getall(self):
308         """ Get all templates
309
310         :rtype : list[wsag_model.Template]
311         """
312         return self.res.getall()
313
314     def getbyid(self, provider_id):
315         """Get a template
316
317         :rtype: wsag_model.Template
318         """
319         return self.res.getbyid(provider_id)
320
321     def create(self, template):
322         """Create a new template
323
324         :param str template: sla template in ws-agreement format.
325         """
326         self.res.create(template)
327
328 class Providers(object):
329
330     def __init__(self, root_url, path=_PROVIDERS_PATH):
331         """Business methods for Providers resource
332         :param str root_url: url to the root of resources
333         :param str path: path to resource from root_url
334
335         The final url to the resource is root_url + "/" + path
336         """
337         resourceurl = _buildpath_(root_url, path)
338         converter = xmlconverter.ProviderConverter()
339         self.res = _Resource(resourceurl, converter)
340
341     def getall(self):
342         """ Get all providers
343
344         :rtype : list[wsag_model.Provider]
345         """
346         return self.res.getall()
347
348     def getbyid(self, provider_id):
349         """Get a provider
350
351         :rtype: wsag_model.Provider
352         """
353         return self.res.getbyid(provider_id)
354
355     def create(self, provider):
356         """Create a new provider
357
358         :type provider: wsag_model.Provider
359         """
360         body = provider.to_xml()
361         return self.res.create(body)
362
363 class Violations(object):
364
365     def __init__(self, root_url, path=_VIOLATIONS_PATH):
366         """Business methods for Violation resource
367         :param str root_url: url to the root of resources
368         :param str path: path to resource from root_url
369
370         The final url to the resource is root_url + "/" + path
371         """
372         resourceurl = _buildpath_(root_url, path)
373         converter = xmlconverter.ViolationConverter()
374         self.res = _Resource(resourceurl, converter)
375
376     def getall(self):
377         """ Get all violations
378         :rtype : list[wsag_model.Violation]
379         """
380         return self.res.getall()
381
382     def getbyid(self, violationid):
383         """Get a violation
384
385         :rtype : wsag_model.Violation
386         """
387         return self.res.getbyid(violationid)
388
389     def getbyagreement(self, agreement_id, term=None):
390         """Get the violations of an agreement.
391
392         :param str agreement_id:
393         :param str term: optional GuaranteeTerm name. If not specified,
394             violations from all terms will be returned
395         :rtype: list[wsag_model.Violation]
396         """
397         return self.res.get(
398             {"agreementId": agreement_id, "guaranteeTerm": term})
399
400
401 class Enforcements(object):
402
403     def __init__(self, root_url, path=_ENFORCEMENTJOBS_PATH):
404         """Business methods for Violation resource
405         :param str root_url: url to the root of resources
406         :param str path: path to resource from root_url
407
408         The final url to the resource is root_url + "/" + path
409         """
410         resourceurl = _buildpath_(root_url, path)
411         converter = xmlconverter.EnforcementConverter()
412         self.res = _Resource(resourceurl, converter)
413
414     def getall(self):
415         """ Get all Enforcements
416         :rtype : list[wsag_model.Violation]
417         """
418         return self.res.getall()
419
420     def getbyagreement(self, agreement_id):
421         """Get the enforcement of an agreement.
422
423         :param str agreement_id:
424         
425         :rtype: list[wsag_model.Enforcement]
426         """
427         return self.res.getbyid(agreement_id)    
428
429
430 def _buildpath_(*paths):
431     return "/".join(paths)
432
433
434 def main():
435     #
436     # Move to test
437     #
438     global rooturl
439     rooturl = "http://127.0.0.1:8080/sla-service"
440     
441
442     c = Factory.templates()
443     #r = c.getall()
444     #r = c.getbyid("noexiste")
445     #r = c.getstatus("agreement03")
446     #print r
447
448     #r = c.getbyconsumer('RandomClient')
449     r = c.getbyid("template02")
450     
451
452     print r
453
454
455 if __name__ == "__main__":
456     main()
457
458