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