From 61eb0f1ee6652db1e3b64a9891d230301f506e67 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Fri, 1 Jan 2016 17:49:12 -0600 Subject: [PATCH] client: Add REST client library --- src/rouse/client.py | 130 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/rouse/client.py diff --git a/src/rouse/client.py b/src/rouse/client.py new file mode 100644 index 0000000..55f4723 --- /dev/null +++ b/src/rouse/client.py @@ -0,0 +1,130 @@ +from __future__ import unicode_literals +from . import host +from six.moves import urllib +import codecs +import json +import os +import six + + +DEFAULT_BASEURL = os.environ.get('ROUSE_URL', 'http://localhost/rouse') + + +class RouseError(Exception): + pass + + +class PUTRequest(urllib.request.Request): + + def get_method(self): + return 'PUT' + + +class DELETERequest(urllib.request.Request): + + def get_method(self): + return 'DELETE' + + +class Rouse(object): + + def __init__(self, baseurl=DEFAULT_BASEURL): + self.baseurl = baseurl.rstrip('/') + + def request(self, path, query=None, data=None, method=None): + if data is not None: + data = urllib.parse.urlencode([(k, v if v is not None else '') + for k, v in six.iteritems(data)]) + data = data.encode('utf-8') + url = urllib.parse.urljoin(self.baseurl, path) + if query: + url += '?' + urllib.parse.urlencode( + [(k, v if v is not None else '') + for k, v in six.iteritems(query)] + ) + + if method == 'DELETE': + req = DELETERequest(url, data) + elif method == 'PUT': + req = PUTRequest(url, data) + else: + req = urllib.request.Request(url, data) + try: + res = urllib.request.urlopen(req) + except urllib.error.HTTPError as e: + try: + content_length = int(e.info()['Content-Length']) + except (KeyError, ValueError): + content_length = 0 + if content_length: + err = json.load(codecs.getreader('utf-8')(e))['error'] + raise RouseError(err) + try: + try: + content_length = int(res.info()['Content-Length']) + except (KeyError, ValueError): + content_length = 0 + if content_length: + return json.load(codecs.getreader('utf-8')(res)) + finally: + res.close() + + def delete(self, path, **keywords): + return self.request(path, keywords, method='DELETE') + + def get(self, path, **keywords): + return self.request(path, keywords) + + def post(self, path, **keywords): + return self.request(path, data=keywords) + + def put(self, path, **keywords): + return self.request(path, data=keywords, method='PUT') + + +class Host(host.Host): + + def __init__(self, macaddr=None, ipaddr=None, hostname=None): + self.id = None + self.macaddr = macaddr + self.ipaddr = ipaddr + self.hostname = hostname + self.rouse = None + + @classmethod + def find(cls, rouse, **keywords): + for item in rouse.get('/', **keywords): + host = cls.from_dict(item) + host.rouse = rouse + yield host + + @classmethod + def from_dict(cls, data): + self = cls() + self.__dict__.update(data) + return self + + @classmethod + def get(cls, rouse, id): + host = cls.from_dict(rouse.get('/{}'.format(id))) + host.rouse = rouse + return host + + def add(self, rouse): + self.rouse = rouse + self.id = self.rouse.post('/', **self.as_dict())['id'] + + def as_dict(self): + data = self.__dict__.copy() + del data['id'] + del data['rouse'] + for key in data: + if key.startswith('_'): + del data[key] + return data + + def delete(self): + self.rouse.delete('/{}'.format(self.id)) + + def update(self): + self.rouse.put('/{}'.format(self.id), **self.as_dict())