SLA code updated and new image for SmartSantander Application Service added
[unfold.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         json_obj = r.json()
285         
286         status = wsag_model.AgreementStatus.json_decode(json_obj)
287
288         return status, r
289     
290     def create(self, agreement):
291         """Create a new agreement
292
293         :param str agreement: sla template in ws-agreement format.
294         """
295         return self.res.create(agreement)
296
297 class Templates(object):
298
299     def __init__(self, root_url, path=_TEMPLATES_PATH):
300         """Business methods for Templates resource
301         :param str root_url: url to the root of resources
302         :param str path: path to resource from root_url
303
304         The final url to the resource is root_url + "/" + path
305         """
306         resourceurl = _buildpath_(root_url, path)
307         converter = xmlconverter.AgreementConverter()
308         self.res = _Resource(resourceurl, converter)
309
310     def getall(self):
311         """ Get all templates
312
313         :rtype : list[wsag_model.Template]
314         """
315         return self.res.getall()
316
317     def getbyid(self, provider_id):
318         """Get a template
319
320         :rtype: wsag_model.Template
321         """
322         return self.res.getbyid(provider_id)
323
324     def create(self, template):
325         """Create a new template
326
327         :param str template: sla template in ws-agreement format.
328         """
329         self.res.create(template)
330
331 class Providers(object):
332
333     def __init__(self, root_url, path=_PROVIDERS_PATH):
334         """Business methods for Providers resource
335         :param str root_url: url to the root of resources
336         :param str path: path to resource from root_url
337
338         The final url to the resource is root_url + "/" + path
339         """
340         resourceurl = _buildpath_(root_url, path)
341         converter = xmlconverter.ProviderConverter()
342         self.res = _Resource(resourceurl, converter)
343
344     def getall(self):
345         """ Get all providers
346
347         :rtype : list[wsag_model.Provider]
348         """
349         return self.res.getall()
350
351     def getbyid(self, provider_id):
352         """Get a provider
353
354         :rtype: wsag_model.Provider
355         """
356         return self.res.getbyid(provider_id)
357
358     def create(self, provider):
359         """Create a new provider
360
361         :type provider: wsag_model.Provider
362         """
363         body = provider.to_xml()
364         return self.res.create(body)
365
366 class Violations(object):
367
368     def __init__(self, root_url, path=_VIOLATIONS_PATH):
369         """Business methods for Violation resource
370         :param str root_url: url to the root of resources
371         :param str path: path to resource from root_url
372
373         The final url to the resource is root_url + "/" + path
374         """
375         resourceurl = _buildpath_(root_url, path)
376         converter = xmlconverter.ViolationConverter()
377         self.res = _Resource(resourceurl, converter)
378
379     def getall(self):
380         """ Get all violations
381         :rtype : list[wsag_model.Violation]
382         """
383         return self.res.getall()
384
385     def getbyid(self, violationid):
386         """Get a violation
387
388         :rtype : wsag_model.Violation
389         """
390         return self.res.getbyid(violationid)
391
392     def getbyagreement(self, agreement_id, term=None):
393         """Get the violations of an agreement.
394
395         :param str agreement_id:
396         :param str term: optional GuaranteeTerm name. If not specified,
397             violations from all terms will be returned
398         :rtype: list[wsag_model.Violation]
399         """
400         return self.res.get(
401             {"agreementId": agreement_id, "guaranteeTerm": term})
402
403
404 class Enforcements(object):
405
406     def __init__(self, root_url, path=_ENFORCEMENTJOBS_PATH):
407         """Business methods for Violation resource
408         :param str root_url: url to the root of resources
409         :param str path: path to resource from root_url
410
411         The final url to the resource is root_url + "/" + path
412         """
413         resourceurl = _buildpath_(root_url, path)
414         converter = xmlconverter.EnforcementConverter()
415         self.res = _Resource(resourceurl, converter)
416
417     def getall(self):
418         """ Get all Enforcements
419         :rtype : list[wsag_model.Violation]
420         """
421         return self.res.getall()
422
423     def getbyagreement(self, agreement_id):
424         """Get the enforcement of an agreement.
425
426         :param str agreement_id:
427         
428         :rtype: list[wsag_model.Enforcement]
429         """
430         return self.res.getbyid(agreement_id)    
431
432
433 def _buildpath_(*paths):
434     return "/".join(paths)
435
436
437 def main():
438     #
439     # Move to test
440     #
441     global rooturl
442     rooturl = "http://127.0.0.1:8080/sla-service"
443     
444
445     c = Factory.templates()
446     #r = c.getall()
447     #r = c.getbyid("noexiste")
448     #r = c.getstatus("agreement03")
449     #print r
450
451     #r = c.getbyconsumer('RandomClient')
452     r = c.getbyid("template02")
453     
454
455     print r
456
457
458 if __name__ == "__main__":
459     main()
460
461