Revision df79206f kamaki/clients/__init__.py

b/kamaki/clients/__init__.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
import json
35
import logging
36

  
37
import requests
38

  
39
from requests.auth import AuthBase
40

  
41

  
42
sendlog = logging.getLogger('clients.send')
43
recvlog = logging.getLogger('clients.recv')
44

  
45

  
46
# Add a convenience json property to the responses
47
def _json(self):
48
    try:
49
        return json.loads(self.content)
50
    except ValueError:
51
        raise ClientError("Invalid JSON reply", self.status_code)
52
requests.Response.json = property(_json)
53

  
54
# Add a convenience status property to the responses
55
def _status(self):
56
    return requests.status_codes._codes[self.status_code][0].upper()
57
requests.Response.status = property(_status)
58

  
59

  
60
class XAuthTokenAuth(AuthBase):
61
    def __init__(self, token):
62
        self.token = token
63
    
64
    def __call__(self, r):
65
        r.headers['X-Auth-Token'] = self.token
66
        return r
67

  
68

  
34 69
class ClientError(Exception):
35 70
    def __init__(self, message, status=0, details=''):
36 71
        self.message = message
37 72
        self.status = status
38 73
        self.details = details
39 74

  
40
    def __int__(self):
41
        return int(self.status)
42 75

  
43
    def __str__(self):
44
        r = self.message
45
        if self.status:
46
            r += "\nHTTP Status: %d" % self.status
47
        if self.details:
48
            r += "\nDetails: \n%s" % self.details
76
class Client(object):
77
    def __init__(self, base_url, token, include=False, verbose=False):
78
        self.base_url = base_url
79
        self.auth = XAuthTokenAuth(token)
80
        self.include = include
81
        self.verbose = verbose
82
    
83
    def raise_for_status(self, r):
84
        if 400 <= r.status_code < 500:
85
            message, sep, details = r.text.partition('\n')
86
        elif 500 <= r.status_code < 600:
87
            message = '%d Server Error' % (r.status_code,)
88
            details = r.text
89
        else:
90
            message = '%d Unknown Error' % (r.status_code,)
91
            details = r.text
92
        
93
        message = message or "HTTP Error %d" % (r.status_code,)
94
        raise ClientError(message, r.status_code, details)
95

  
96
    def request(self, method, path, **kwargs):
97
        raw = kwargs.pop('raw', False)
98
        success = kwargs.pop('success', 200)
99
        if 'json' in kwargs:
100
            data = json.dumps(kwargs.pop('json'))
101
            kwargs['data'] = data
102
            headers = kwargs.setdefault('headers', {})
103
            headers['content-type'] = 'application/json'
104

  
105
        url = self.base_url + path
106
        kwargs.setdefault('auth', self.auth)
107
        r = requests.request(method, url, **kwargs)
108
        
109
        req = r.request
110
        sendlog.info('%s %s', req.method, req.url)
111
        for key, val in req.headers.items():
112
            sendlog.info('%s: %s', key, val)
113
        sendlog.info('')
114
        if req.data:
115
            sendlog.info('%s', req.data)
116
        
117
        recvlog.info('%d %s', r.status_code, r.status)
118
        for key, val in r.headers.items():
119
            recvlog.info('%s: %s', key, val)
120
        recvlog.info('')
121
        if not raw and r.text:
122
            recvlog.debug(r.text)
123
        
124
        if success is not None:
125
            # Success can either be an in or a collection
126
            success = (success,) if isinstance(success, int) else success
127
            if r.status_code not in success:
128
                self.raise_for_status(r)
129

  
49 130
        return r
50 131

  
132
    def delete(self, path, **kwargs):
133
        return self.request('delete', path, **kwargs)
134

  
135
    def get(self, path, **kwargs):
136
        return self.request('get', path, **kwargs)
137

  
138
    def head(self, path, **kwargs):
139
        return self.request('head', path, **kwargs)
140

  
141
    def post(self, path, **kwargs):
142
        return self.request('post', path, **kwargs)
143

  
144
    def put(self, path, **kwargs):
145
        return self.request('put', path, **kwargs)
146

  
51 147

  
52
from .compute import ComputeClient
53
from .image import ImageClient
54
from .storage import StorageClient
55
from .cyclades import CycladesClient
56
from .pithos import PithosClient
148
from .compute import ComputeClient as compute
149
from .image import ImageClient as image
150
from .storage import StorageClient as storage
151
from .cyclades import CycladesClient as cyclades
152
from .pithos import PithosClient as pithos

Also available in: Unified diff