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