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
121         return result
122     
123     def post(self, path, data=None, **kwargs):
124         """Just a wrapper over request.post, just in case
125
126         :rtype : request.Response
127         :param str path: remaining path from root url;
128             empty if desired path equal to rooturl.
129         :param dict[str, str] kwargs: arguments to requests.post
130
131         Example:
132             c = Client("http://localhost:8080/service")
133             c.post(
134                 '/resource',
135                 '{ "id": "1", "name": "provider-a" }',
136                 headers = {
137                     "content-type": "application/json",
138                     "accept": "application/xml"
139                 }
140             )
141         """
142         url = _buildpath_(self.rooturl, path)
143         kwargs["auth"] = HTTPBasicAuth(settings.SLA_MANAGER_USER, settings.SLA_MANAGER_PASSWORD)
144         result = requests.post(url, data, **kwargs)
145         location = result.headers["location"] \
146             if "location" in result.headers else "<null>"
147         print "POST {} {} Location: {}".format(
148             result.url, result.status_code, location)
149         return result
150
151     
152
153 class _Resource(object):
154
155     def __init__(self, url, converter):
156         """Provides some common operations over resources.
157
158         The operations return a structured representation of the resource.
159
160         :param str url: url to the resource
161         :param Converter converter: resouce xml converter
162
163         Some attributes are initialized to be used from the owner if needed:
164         * client: Client instance
165         * converter: resource xml converter
166         * listconverter: list of resources xml converter
167         """
168         self.client = Client(url)
169         self.converter = converter
170         self.listconverter = xmlconverter.ListConverter(self.converter)
171
172     @staticmethod
173     def _processresult(r, converter):
174
175         """Generic processing of the REST call.
176
177          If no errors, tries to convert the result to a destination entity.
178
179         :param r requests:
180         :param converter Converter:
181         """
182         if r.status_code == 404:
183             return None
184
185         content_type = r.headers.get('content-type', '')
186
187         print("content-type = " + content_type)
188         if content_type == 'application/json':
189             result = r.json()
190         elif content_type == 'application/xml':
191             xml = r.text
192             result = xmlconverter.convertstring(converter, xml)
193         else:
194             result = r.text
195         return result
196
197     def getall(self):
198         """Get all resources
199
200         """
201         r = self.client.get("")
202         resources = self._processresult(r, self.listconverter)
203         return resources, r
204
205     def getbyid(self, id):
206         """Get resource 'id'"""
207         r = self.client.get(id)
208         resource = _Resource._processresult(r, self.converter)
209         return resource, r
210
211     def get(self, params):
212         """Generic query over resource: GET /resource?q1=v1&q2=v2...
213
214         :param dict[str,str] params: values to pass as get parameters
215         """
216         r = self.client.get("", params=params)
217         resources = self._processresult(r, self.listconverter)
218         return resources, r
219
220     def create(self, body, **kwargs):
221         """Creates (POST method) a resource.
222
223         It should be convenient to set content-type header.
224
225         Usage:
226             resource.create(body, headers={'content-type': 'application/xml'})
227         """
228         r = self.client.post("", body, **kwargs)
229         r.raise_for_status()
230         return r
231
232
233 class Agreements(object):
234
235     def __init__(self, root_url, path=_AGREEMENTS_PATH):
236         """Business methods for Agreement resource
237         :param str root_url: url to the root of resources
238         :param str path: path to resource from root_url
239
240         The final url to the resource is root_url + "/" + path
241         """
242         resourceurl = _buildpath_(root_url, path)
243         converter = xmlconverter.AgreementConverter()
244         self.res = _Resource(resourceurl, converter)
245
246     def getall(self):
247         """
248         Get all agreements
249
250         :rtype : list[wsag_model.Agreement]
251         """
252         return self.res.getall()
253
254     def getbyid(self, agreementid):
255         """Get an agreement
256
257         :rtype : wsag_model.Agreement
258         """
259         return self.res.getbyid(agreementid)
260
261     def getbyconsumer(self, consumerid):
262         """Get a consumer's agreements
263
264         :rtype : list[wsag_model.Agreement]
265         """
266         return self.res.get(dict(consumerId=consumerid))
267
268     def getbyprovider(self, providerid):
269         """Get the agreements served by a provider
270
271         :rtype : list[wsag_model.Agreement]
272         """
273         return self.res.get(dict(providerId=providerid))
274
275     def getstatus(self, agreementid):
276         """Get guarantee status of an agreement
277
278         :param str agreementid :
279         :rtype : wsag_model.AgreementStatus
280         """
281         path = _buildpath_(agreementid, "guaranteestatus")
282         r = self.res.client.get(path, headers={'accept': 'application/json'})
283
284         print "---- JSON Response ----"
285         print r.text
286
287         json_obj = r.json()
288         print "-- json_obj() --"
289         print type(json_obj)
290         print json_obj
291         
292         status = wsag_model.AgreementStatus.json_decode(json_obj)
293
294         print "---- STATUS ----"
295         print status
296
297         return status, r
298     
299     def create(self, agreement):
300         """Create a new agreement
301
302         :param str agreement: sla template in ws-agreement format.
303         """
304         return self.res.create(agreement)
305
306 class Templates(object):
307
308     def __init__(self, root_url, path=_TEMPLATES_PATH):
309         """Business methods for Templates resource
310         :param str root_url: url to the root of resources
311         :param str path: path to resource from root_url
312
313         The final url to the resource is root_url + "/" + path
314         """
315         resourceurl = _buildpath_(root_url, path)
316         converter = xmlconverter.AgreementConverter()
317         self.res = _Resource(resourceurl, converter)
318
319     def getall(self):
320         """ Get all templates
321
322         :rtype : list[wsag_model.Template]
323         """
324         return self.res.getall()
325
326     def getbyid(self, provider_id):
327         """Get a template
328
329         :rtype: wsag_model.Template
330         """
331         return self.res.getbyid(provider_id)
332
333     def create(self, template):
334         """Create a new template
335
336         :param str template: sla template in ws-agreement format.
337         """
338         self.res.create(template)
339
340 class Providers(object):
341
342     def __init__(self, root_url, path=_PROVIDERS_PATH):
343         """Business methods for Providers resource
344         :param str root_url: url to the root of resources
345         :param str path: path to resource from root_url
346
347         The final url to the resource is root_url + "/" + path
348         """
349         resourceurl = _buildpath_(root_url, path)
350         converter = xmlconverter.ProviderConverter()
351         self.res = _Resource(resourceurl, converter)
352
353     def getall(self):
354         """ Get all providers
355
356         :rtype : list[wsag_model.Provider]
357         """
358         return self.res.getall()
359
360     def getbyid(self, provider_id):
361         """Get a provider
362
363         :rtype: wsag_model.Provider
364         """
365         return self.res.getbyid(provider_id)
366
367     def create(self, provider):
368         """Create a new provider
369
370         :type provider: wsag_model.Provider
371         """
372         body = provider.to_xml()
373         return self.res.create(body)
374
375 class Violations(object):
376
377     def __init__(self, root_url, path=_VIOLATIONS_PATH):
378         """Business methods for Violation resource
379         :param str root_url: url to the root of resources
380         :param str path: path to resource from root_url
381
382         The final url to the resource is root_url + "/" + path
383         """
384         resourceurl = _buildpath_(root_url, path)
385         converter = xmlconverter.ViolationConverter()
386         self.res = _Resource(resourceurl, converter)
387
388     def getall(self):
389         """ Get all violations
390         :rtype : list[wsag_model.Violation]
391         """
392         return self.res.getall()
393
394     def getbyid(self, violationid):
395         """Get a violation
396
397         :rtype : wsag_model.Violation
398         """
399         return self.res.getbyid(violationid)
400
401     def getbyagreement(self, agreement_id, term=None):
402         """Get the violations of an agreement.
403
404         :param str agreement_id:
405         :param str term: optional GuaranteeTerm name. If not specified,
406             violations from all terms will be returned
407         :rtype: list[wsag_model.Violation]
408         """
409         return self.res.get(
410             {"agreementId": agreement_id, "guaranteeTerm": term})
411
412
413 class Enforcements(object):
414
415     def __init__(self, root_url, path=_ENFORCEMENTJOBS_PATH):
416         """Business methods for Violation resource
417         :param str root_url: url to the root of resources
418         :param str path: path to resource from root_url
419
420         The final url to the resource is root_url + "/" + path
421         """
422         resourceurl = _buildpath_(root_url, path)
423         converter = xmlconverter.EnforcementConverter()
424         self.res = _Resource(resourceurl, converter)
425
426     def getall(self):
427         """ Get all Enforcements
428         :rtype : list[wsag_model.Violation]
429         """
430         return self.res.getall()
431
432     def getbyagreement(self, agreement_id):
433         """Get the enforcement of an agreement.
434
435         :param str agreement_id:
436         
437         :rtype: list[wsag_model.Enforcement]
438         """
439         return self.res.getbyid(agreement_id)    
440
441
442 def _buildpath_(*paths):
443     return "/".join(paths)
444
445
446 def main():
447     #
448     # Move to test
449     #
450     global rooturl
451     rooturl = "http://127.0.0.1:8080/sla-service"
452     
453
454     c = Factory.templates()
455     #r = c.getall()
456     #r = c.getbyid("noexiste")
457     #r = c.getstatus("agreement03")
458     #print r
459
460     #r = c.getbyconsumer('RandomClient')
461     r = c.getbyid("template02")
462     
463
464     print r
465
466
467 if __name__ == "__main__":
468     main()
469
470