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