Statistics
| Branch: | Tag: | Revision:

root / pithos / lib / client.py @ 6749c3bd

History | View | Annotate | Download (17.8 kB)

1 a4c10cbc Sofia Papagiannaki
# Copyright 2011 GRNET S.A. All rights reserved.
2 a4c10cbc Sofia Papagiannaki
# 
3 a4c10cbc Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 a4c10cbc Sofia Papagiannaki
# without modification, are permitted provided that the following
5 a4c10cbc Sofia Papagiannaki
# conditions are met:
6 a4c10cbc Sofia Papagiannaki
# 
7 a4c10cbc Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 a4c10cbc Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 a4c10cbc Sofia Papagiannaki
#      disclaimer.
10 a4c10cbc Sofia Papagiannaki
# 
11 a4c10cbc Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 a4c10cbc Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 a4c10cbc Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 a4c10cbc Sofia Papagiannaki
#      provided with the distribution.
15 a4c10cbc Sofia Papagiannaki
# 
16 a4c10cbc Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a4c10cbc Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a4c10cbc Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a4c10cbc Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a4c10cbc Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a4c10cbc Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a4c10cbc Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a4c10cbc Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a4c10cbc Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a4c10cbc Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a4c10cbc Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a4c10cbc Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 a4c10cbc Sofia Papagiannaki
# 
29 a4c10cbc Sofia Papagiannaki
# The views and conclusions contained in the software and
30 a4c10cbc Sofia Papagiannaki
# documentation are those of the authors and should not be
31 a4c10cbc Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 a4c10cbc Sofia Papagiannaki
# or implied, of GRNET S.A.
33 a4c10cbc Sofia Papagiannaki
34 ec1b8d3e Sofia Papagiannaki
from httplib import HTTPConnection, HTTP
35 ec1b8d3e Sofia Papagiannaki
from sys import stdin
36 3f5b02c7 Sofia Papagiannaki
37 3f5b02c7 Sofia Papagiannaki
import json
38 ec1b8d3e Sofia Papagiannaki
import types
39 0ea1dcc4 Sofia Papagiannaki
import socket
40 e3fd7f91 Sofia Papagiannaki
import pithos.api.faults
41 e3fd7f91 Sofia Papagiannaki
42 e3fd7f91 Sofia Papagiannaki
ERROR_CODES = {304:'Not Modified',
43 e3fd7f91 Sofia Papagiannaki
               400:'Bad Request',
44 e3fd7f91 Sofia Papagiannaki
               401:'Unauthorized',
45 e3fd7f91 Sofia Papagiannaki
               404:'Not Found',
46 e3fd7f91 Sofia Papagiannaki
               409:'Conflict',
47 e3fd7f91 Sofia Papagiannaki
               411:'Length Required',
48 e3fd7f91 Sofia Papagiannaki
               412:'Precondition Failed',
49 e3fd7f91 Sofia Papagiannaki
               416:'Range Not Satisfiable',
50 e3fd7f91 Sofia Papagiannaki
               422:'Unprocessable Entity',
51 e3fd7f91 Sofia Papagiannaki
               503:'Service Unavailable'}
52 3f5b02c7 Sofia Papagiannaki
53 3f5b02c7 Sofia Papagiannaki
class Fault(Exception):
54 e3fd7f91 Sofia Papagiannaki
    def __init__(self, data='', status=None):
55 e3fd7f91 Sofia Papagiannaki
        if data == '' and status in ERROR_CODES.keys():
56 e3fd7f91 Sofia Papagiannaki
            data = ERROR_CODES[status]
57 3f5b02c7 Sofia Papagiannaki
        Exception.__init__(self, data)
58 3f5b02c7 Sofia Papagiannaki
        self.data = data
59 e3fd7f91 Sofia Papagiannaki
        self.status = status
60 3f5b02c7 Sofia Papagiannaki
61 3f5b02c7 Sofia Papagiannaki
class Client(object):
62 bcb7c5a8 Sofia Papagiannaki
    def __init__(self, host, token, account, api='v1', verbose=False, debug=False):
63 3f5b02c7 Sofia Papagiannaki
        """`host` can also include a port, e.g '127.0.0.1:8000'."""
64 3f5b02c7 Sofia Papagiannaki
        
65 3f5b02c7 Sofia Papagiannaki
        self.host = host
66 3f5b02c7 Sofia Papagiannaki
        self.account = account
67 3f5b02c7 Sofia Papagiannaki
        self.api = api
68 3f5b02c7 Sofia Papagiannaki
        self.verbose = verbose or debug
69 3f5b02c7 Sofia Papagiannaki
        self.debug = debug
70 bcb7c5a8 Sofia Papagiannaki
        self.token = token
71 f7ab99df Sofia Papagiannaki
    
72 ec1b8d3e Sofia Papagiannaki
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
73 ec1b8d3e Sofia Papagiannaki
                          blocksize=1024):
74 ec1b8d3e Sofia Papagiannaki
        http = HTTPConnection(self.host)
75 ec1b8d3e Sofia Papagiannaki
        
76 ec1b8d3e Sofia Papagiannaki
        # write header
77 ec1b8d3e Sofia Papagiannaki
        path = '/%s/%s%s' % (self.api, self.account, path)
78 ec1b8d3e Sofia Papagiannaki
        http.putrequest(method, path)
79 bcb7c5a8 Sofia Papagiannaki
        http.putheader('X-Auth-Token', self.token)
80 ec1b8d3e Sofia Papagiannaki
        http.putheader('Content-Type', 'application/octet-stream')
81 ec1b8d3e Sofia Papagiannaki
        http.putheader('Transfer-Encoding', 'chunked')
82 ec1b8d3e Sofia Papagiannaki
        if headers:
83 ec1b8d3e Sofia Papagiannaki
            for header,value in headers.items():
84 ec1b8d3e Sofia Papagiannaki
                http.putheader(header, value)
85 ec1b8d3e Sofia Papagiannaki
        http.endheaders()
86 ec1b8d3e Sofia Papagiannaki
        
87 ec1b8d3e Sofia Papagiannaki
        # write body
88 ec1b8d3e Sofia Papagiannaki
        data = ''
89 ec1b8d3e Sofia Papagiannaki
        while True:
90 ec1b8d3e Sofia Papagiannaki
            if f.closed:
91 ec1b8d3e Sofia Papagiannaki
                break
92 ec1b8d3e Sofia Papagiannaki
            block = f.read(blocksize)
93 ec1b8d3e Sofia Papagiannaki
            if block == '':
94 ec1b8d3e Sofia Papagiannaki
                break
95 ec1b8d3e Sofia Papagiannaki
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
96 ec1b8d3e Sofia Papagiannaki
            try:
97 ec1b8d3e Sofia Papagiannaki
                http.send(data)
98 ec1b8d3e Sofia Papagiannaki
            except:
99 ec1b8d3e Sofia Papagiannaki
                #retry
100 ec1b8d3e Sofia Papagiannaki
                http.send(data)
101 ec1b8d3e Sofia Papagiannaki
        data = '0x0\r\n'
102 ec1b8d3e Sofia Papagiannaki
        try:
103 ec1b8d3e Sofia Papagiannaki
            http.send(data)
104 ec1b8d3e Sofia Papagiannaki
        except:
105 ec1b8d3e Sofia Papagiannaki
            #retry
106 ec1b8d3e Sofia Papagiannaki
            http.send(data)
107 ec1b8d3e Sofia Papagiannaki
        
108 ec1b8d3e Sofia Papagiannaki
        # get response
109 ec1b8d3e Sofia Papagiannaki
        resp = http.getresponse()
110 e3fd7f91 Sofia Papagiannaki
        
111 ec1b8d3e Sofia Papagiannaki
        headers = dict(resp.getheaders())
112 ec1b8d3e Sofia Papagiannaki
        
113 ec1b8d3e Sofia Papagiannaki
        if self.verbose:
114 ec1b8d3e Sofia Papagiannaki
            print '%d %s' % (resp.status, resp.reason)
115 ec1b8d3e Sofia Papagiannaki
            for key, val in headers.items():
116 ec1b8d3e Sofia Papagiannaki
                print '%s: %s' % (key.capitalize(), val)
117 ec1b8d3e Sofia Papagiannaki
            print
118 ec1b8d3e Sofia Papagiannaki
        
119 f7ab99df Sofia Papagiannaki
        length = resp.getheader('Content-length', None)
120 6cd1831a Sofia Papagiannaki
        data = resp.read(length)
121 ec1b8d3e Sofia Papagiannaki
        if self.debug:
122 ec1b8d3e Sofia Papagiannaki
            print data
123 ec1b8d3e Sofia Papagiannaki
            print
124 ec1b8d3e Sofia Papagiannaki
        
125 e3fd7f91 Sofia Papagiannaki
        if int(resp.status) in ERROR_CODES.keys():
126 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(resp.status))
127 e3fd7f91 Sofia Papagiannaki
        
128 f7ab99df Sofia Papagiannaki
        #print '*',  resp.status, headers, data
129 ec1b8d3e Sofia Papagiannaki
        return resp.status, headers, data
130 f7ab99df Sofia Papagiannaki
    
131 d2d5c360 Sofia Papagiannaki
    def req(self, method, path, body=None, headers=None, format='text',
132 d2d5c360 Sofia Papagiannaki
            params=None):
133 d2d5c360 Sofia Papagiannaki
        full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path,
134 d2d5c360 Sofia Papagiannaki
                                            format)
135 3f5b02c7 Sofia Papagiannaki
        if params:
136 3f5b02c7 Sofia Papagiannaki
            for k,v in params.items():
137 ec1b8d3e Sofia Papagiannaki
                if v:
138 ec1b8d3e Sofia Papagiannaki
                    full_path = '%s&%s=%s' %(full_path, k, v)
139 6749c3bd Sofia Papagiannaki
                else:
140 6749c3bd Sofia Papagiannaki
                    full_path = '%s&%s' %(full_path, k)
141 3f5b02c7 Sofia Papagiannaki
        conn = HTTPConnection(self.host)
142 3f5b02c7 Sofia Papagiannaki
        
143 d2d5c360 Sofia Papagiannaki
        #encode whitespace
144 d2d5c360 Sofia Papagiannaki
        full_path = full_path.replace(' ', '%20')
145 ec1b8d3e Sofia Papagiannaki
        
146 3f5b02c7 Sofia Papagiannaki
        kwargs = {}
147 3f5b02c7 Sofia Papagiannaki
        kwargs['headers'] = headers or {}
148 bcb7c5a8 Sofia Papagiannaki
        kwargs['headers']['X-Auth-Token'] = self.token
149 ec1b8d3e Sofia Papagiannaki
        if not headers or \
150 ec1b8d3e Sofia Papagiannaki
        'Transfer-Encoding' not in headers \
151 ec1b8d3e Sofia Papagiannaki
        or headers['Transfer-Encoding'] != 'chunked':
152 ec1b8d3e Sofia Papagiannaki
            kwargs['headers']['Content-Length'] = len(body) if body else 0
153 3f5b02c7 Sofia Papagiannaki
        if body:
154 3f5b02c7 Sofia Papagiannaki
            kwargs['body'] = body
155 4013a851 Sofia Papagiannaki
        else:
156 4013a851 Sofia Papagiannaki
            kwargs['headers']['Content-Type'] = ''
157 bcb7c5a8 Sofia Papagiannaki
        kwargs['headers'].setdefault('Content-Type', 'application/octet-stream')
158 e3fd7f91 Sofia Papagiannaki
        try:
159 e3fd7f91 Sofia Papagiannaki
            conn.request(method, full_path, **kwargs)
160 e3fd7f91 Sofia Papagiannaki
        except socket.error, e:
161 e3fd7f91 Sofia Papagiannaki
            raise Fault(status=503)
162 e3fd7f91 Sofia Papagiannaki
            
163 3f5b02c7 Sofia Papagiannaki
        resp = conn.getresponse()
164 3f5b02c7 Sofia Papagiannaki
        headers = dict(resp.getheaders())
165 3f5b02c7 Sofia Papagiannaki
        
166 3f5b02c7 Sofia Papagiannaki
        if self.verbose:
167 3f5b02c7 Sofia Papagiannaki
            print '%d %s' % (resp.status, resp.reason)
168 3f5b02c7 Sofia Papagiannaki
            for key, val in headers.items():
169 3f5b02c7 Sofia Papagiannaki
                print '%s: %s' % (key.capitalize(), val)
170 3f5b02c7 Sofia Papagiannaki
            print
171 3f5b02c7 Sofia Papagiannaki
        
172 f7ab99df Sofia Papagiannaki
        length = resp.getheader('Content-length', None)
173 6cd1831a Sofia Papagiannaki
        data = resp.read(length)
174 3f5b02c7 Sofia Papagiannaki
        if self.debug:
175 3f5b02c7 Sofia Papagiannaki
            print data
176 3f5b02c7 Sofia Papagiannaki
            print
177 3f5b02c7 Sofia Papagiannaki
        
178 e3fd7f91 Sofia Papagiannaki
        if int(resp.status) in ERROR_CODES.keys():
179 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(resp.status))
180 e3fd7f91 Sofia Papagiannaki
        
181 e3fd7f91 Sofia Papagiannaki
        #print '*',  resp.status, headers, data
182 3f5b02c7 Sofia Papagiannaki
        return resp.status, headers, data
183 f7ab99df Sofia Papagiannaki
    
184 3f5b02c7 Sofia Papagiannaki
    def delete(self, path, format='text'):
185 3f5b02c7 Sofia Papagiannaki
        return self.req('DELETE', path, format=format)
186 f7ab99df Sofia Papagiannaki
    
187 3f5b02c7 Sofia Papagiannaki
    def get(self, path, format='text', headers=None, params=None):
188 3f5b02c7 Sofia Papagiannaki
        return self.req('GET', path, headers=headers, format=format,
189 ec1b8d3e Sofia Papagiannaki
                        params=params)
190 f7ab99df Sofia Papagiannaki
    
191 d2d5c360 Sofia Papagiannaki
    def head(self, path, format='text', params=None):
192 d2d5c360 Sofia Papagiannaki
        return self.req('HEAD', path, format=format, params=params)
193 f7ab99df Sofia Papagiannaki
    
194 6749c3bd Sofia Papagiannaki
    def post(self, path, body=None, format='text', headers=None, params=None):
195 6749c3bd Sofia Papagiannaki
        return self.req('POST', path, body, headers=headers, format=format,
196 6749c3bd Sofia Papagiannaki
                        params=params)
197 f7ab99df Sofia Papagiannaki
    
198 3f5b02c7 Sofia Papagiannaki
    def put(self, path, body=None, format='text', headers=None):
199 3f5b02c7 Sofia Papagiannaki
        return self.req('PUT', path, body, headers=headers, format=format)
200 f7ab99df Sofia Papagiannaki
    
201 3f5b02c7 Sofia Papagiannaki
    def _list(self, path, detail=False, params=None, headers=None):
202 3f5b02c7 Sofia Papagiannaki
        format = 'json' if detail else 'text'
203 3f5b02c7 Sofia Papagiannaki
        status, headers, data = self.get(path, format=format, headers=headers,
204 3f5b02c7 Sofia Papagiannaki
                                         params=params)
205 ec1b8d3e Sofia Papagiannaki
        if detail:
206 bcb7c5a8 Sofia Papagiannaki
            data = json.loads(data) if data else ''
207 ec1b8d3e Sofia Papagiannaki
        else:
208 b41a95a0 Sofia Papagiannaki
            data = data.strip().split('\n')
209 3f5b02c7 Sofia Papagiannaki
        return data
210 f7ab99df Sofia Papagiannaki
    
211 d2d5c360 Sofia Papagiannaki
    def _get_metadata(self, path, prefix=None, params=None):
212 d2d5c360 Sofia Papagiannaki
        status, headers, data = self.head(path, params=params)
213 f7ab99df Sofia Papagiannaki
        prefixlen = len(prefix) if prefix else 0
214 3f5b02c7 Sofia Papagiannaki
        meta = {}
215 3f5b02c7 Sofia Papagiannaki
        for key, val in headers.items():
216 0ea1dcc4 Sofia Papagiannaki
            if prefix and not key.startswith(prefix):
217 0ea1dcc4 Sofia Papagiannaki
                continue
218 0ea1dcc4 Sofia Papagiannaki
            elif prefix and key.startswith(prefix):
219 3f5b02c7 Sofia Papagiannaki
                key = key[prefixlen:]
220 0ea1dcc4 Sofia Papagiannaki
            meta[key] = val
221 3f5b02c7 Sofia Papagiannaki
        return meta
222 f7ab99df Sofia Papagiannaki
    
223 f7ab99df Sofia Papagiannaki
    def _update_metadata(self, path, entity, **meta):
224 f7ab99df Sofia Papagiannaki
        """
225 6749c3bd Sofia Papagiannaki
        adds new and updates the values of previously set metadata
226 f7ab99df Sofia Papagiannaki
        """
227 6749c3bd Sofia Papagiannaki
        params = {'update':None}
228 f7ab99df Sofia Papagiannaki
        headers = {}
229 6749c3bd Sofia Papagiannaki
        prefix = 'x-%s-meta-' % entity
230 6749c3bd Sofia Papagiannaki
        for k,v in meta.items():
231 6749c3bd Sofia Papagiannaki
            k = '%s%s' % (prefix, k)
232 6749c3bd Sofia Papagiannaki
            k = '-'.join(elem.capitalize() for elem in k.split('-'))
233 6749c3bd Sofia Papagiannaki
            headers[k] = v
234 6749c3bd Sofia Papagiannaki
        self.post(path, headers=headers, params=params)
235 f7ab99df Sofia Papagiannaki
    
236 f7ab99df Sofia Papagiannaki
    def _delete_metadata(self, path, entity, meta=[]):
237 f7ab99df Sofia Papagiannaki
        """
238 f7ab99df Sofia Papagiannaki
        delete previously set metadata
239 f7ab99df Sofia Papagiannaki
        """
240 961f2fbe Sofia Papagiannaki
        prefix = 'x-%s-meta-' % entity
241 961f2fbe Sofia Papagiannaki
        prev_meta = self._get_metadata(path, prefix)
242 f7ab99df Sofia Papagiannaki
        headers = {}
243 f7ab99df Sofia Papagiannaki
        for key, val in prev_meta.items():
244 961f2fbe Sofia Papagiannaki
            if key in meta:
245 f7ab99df Sofia Papagiannaki
                continue
246 961f2fbe Sofia Papagiannaki
            key = '%s%s' % (prefix, key)
247 961f2fbe Sofia Papagiannaki
            key = '-'.join(elem.capitalize() for elem in key.split('-'))
248 961f2fbe Sofia Papagiannaki
            headers[key] = val
249 3f5b02c7 Sofia Papagiannaki
        self.post(path, headers=headers)
250 f7ab99df Sofia Papagiannaki
    
251 3f5b02c7 Sofia Papagiannaki
    # Storage Account Services
252 f7ab99df Sofia Papagiannaki
    
253 ec1b8d3e Sofia Papagiannaki
    def list_containers(self, detail=False, params=None, headers=None):
254 3f5b02c7 Sofia Papagiannaki
        return self._list('', detail, params, headers)
255 f7ab99df Sofia Papagiannaki
    
256 d2d5c360 Sofia Papagiannaki
    def account_metadata(self, restricted=False, until=None):
257 f7ab99df Sofia Papagiannaki
        prefix = 'x-account-meta-' if restricted else None
258 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
259 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('', prefix, params=params)
260 f7ab99df Sofia Papagiannaki
    
261 3f5b02c7 Sofia Papagiannaki
    def update_account_metadata(self, **meta):
262 f7ab99df Sofia Papagiannaki
        self._update_metadata('', 'account', **meta)
263 f7ab99df Sofia Papagiannaki
        
264 f7ab99df Sofia Papagiannaki
    def delete_account_metadata(self, meta=[]):
265 f7ab99df Sofia Papagiannaki
        self._delete_metadata('', 'account', meta)
266 f7ab99df Sofia Papagiannaki
    
267 bcb7c5a8 Sofia Papagiannaki
    def set_account_groups(self, **groups):
268 d3fd269f Sofia Papagiannaki
        headers = {}
269 d3fd269f Sofia Papagiannaki
        for key, val in groups.items():
270 d3fd269f Sofia Papagiannaki
            headers['X-Account-Group-%s' % key.capitalize()] = val
271 d3fd269f Sofia Papagiannaki
        self.post('', headers=headers)
272 d3fd269f Sofia Papagiannaki
    
273 3f5b02c7 Sofia Papagiannaki
    # Storage Container Services
274 f7ab99df Sofia Papagiannaki
    
275 a4c10cbc Sofia Papagiannaki
    def _filter(self, l, d):
276 a4c10cbc Sofia Papagiannaki
        """
277 a4c10cbc Sofia Papagiannaki
        filter out from l elements having the metadata values provided
278 a4c10cbc Sofia Papagiannaki
        """
279 a4c10cbc Sofia Papagiannaki
        ll = l
280 a4c10cbc Sofia Papagiannaki
        for elem in l:
281 a4c10cbc Sofia Papagiannaki
            if type(elem) == types.DictionaryType:
282 a4c10cbc Sofia Papagiannaki
                for key in d.keys():
283 a4c10cbc Sofia Papagiannaki
                    k = 'x_object_meta_%s' % key
284 a4c10cbc Sofia Papagiannaki
                    if k in elem.keys() and elem[k] == d[key]:
285 a4c10cbc Sofia Papagiannaki
                        ll.remove(elem)
286 a4c10cbc Sofia Papagiannaki
                        break
287 a4c10cbc Sofia Papagiannaki
        return ll
288 a4c10cbc Sofia Papagiannaki
    
289 a4c10cbc Sofia Papagiannaki
    def _filter_trashed(self, l):
290 a4c10cbc Sofia Papagiannaki
        return self._filter(l, {'trash':'true'})
291 a4c10cbc Sofia Papagiannaki
    
292 bcb7c5a8 Sofia Papagiannaki
    def list_objects(self, container, detail=False, headers=None,
293 bcb7c5a8 Sofia Papagiannaki
                     include_trashed=False, **params):
294 a4c10cbc Sofia Papagiannaki
        l = self._list('/' + container, detail, params, headers)
295 a4c10cbc Sofia Papagiannaki
        if not include_trashed:
296 a4c10cbc Sofia Papagiannaki
            l = self._filter_trashed(l)
297 a4c10cbc Sofia Papagiannaki
        return l
298 f7ab99df Sofia Papagiannaki
    
299 bcb7c5a8 Sofia Papagiannaki
    def create_container(self, container, headers=None, **meta):
300 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
301 f89e3cf4 Sofia Papagiannaki
            headers['X-Container-Meta-%s' %k.strip().upper()] = v.strip()
302 ec1b8d3e Sofia Papagiannaki
        status, header, data = self.put('/' + container, headers=headers)
303 3f5b02c7 Sofia Papagiannaki
        if status == 202:
304 3f5b02c7 Sofia Papagiannaki
            return False
305 3f5b02c7 Sofia Papagiannaki
        elif status != 201:
306 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(status))
307 3f5b02c7 Sofia Papagiannaki
        return True
308 f7ab99df Sofia Papagiannaki
    
309 3f5b02c7 Sofia Papagiannaki
    def delete_container(self, container):
310 3f5b02c7 Sofia Papagiannaki
        self.delete('/' + container)
311 f7ab99df Sofia Papagiannaki
    
312 d2d5c360 Sofia Papagiannaki
    def retrieve_container_metadata(self, container, restricted=False,
313 d2d5c360 Sofia Papagiannaki
                                    until=None):
314 f7ab99df Sofia Papagiannaki
        prefix = 'x-container-meta-' if restricted else None
315 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
316 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('/%s' % container, prefix, params=params)
317 f7ab99df Sofia Papagiannaki
    
318 3f5b02c7 Sofia Papagiannaki
    def update_container_metadata(self, container, **meta):
319 f7ab99df Sofia Papagiannaki
        self._update_metadata('/' + container, 'container', **meta)
320 f7ab99df Sofia Papagiannaki
        
321 f7ab99df Sofia Papagiannaki
    def delete_container_metadata(self, container, meta=[]):
322 f7ab99df Sofia Papagiannaki
        path = '/%s' % (container)
323 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'container', meta)
324 f7ab99df Sofia Papagiannaki
    
325 bcb7c5a8 Sofia Papagiannaki
    def set_container_policies(self, container, **policies):
326 bcb7c5a8 Sofia Papagiannaki
        path = '/%s' % (container)
327 bcb7c5a8 Sofia Papagiannaki
        headers = {}
328 bcb7c5a8 Sofia Papagiannaki
        print ''
329 bcb7c5a8 Sofia Papagiannaki
        for key, val in policies.items():
330 bcb7c5a8 Sofia Papagiannaki
            headers['X-Container-Policy-%s' % key.capitalize()] = val
331 bcb7c5a8 Sofia Papagiannaki
        self.post(path, headers=headers)
332 bcb7c5a8 Sofia Papagiannaki
    
333 3f5b02c7 Sofia Papagiannaki
    # Storage Object Services
334 f7ab99df Sofia Papagiannaki
    
335 8fe01d72 Sofia Papagiannaki
    def retrieve_object(self, container, object, detail=False, headers=None,
336 8fe01d72 Sofia Papagiannaki
                        version=None):
337 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
338 ec1b8d3e Sofia Papagiannaki
        format = 'json' if detail else 'text'
339 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None 
340 8fe01d72 Sofia Papagiannaki
        status, headers, data = self.get(path, format, headers, params)
341 3f5b02c7 Sofia Papagiannaki
        return data
342 f7ab99df Sofia Papagiannaki
    
343 f7ab99df Sofia Papagiannaki
    def create_directory_marker(self, container, object):
344 f7ab99df Sofia Papagiannaki
        if not object:
345 f7ab99df Sofia Papagiannaki
            raise Fault('Directory markers have to be nested in a container')
346 f7ab99df Sofia Papagiannaki
        h = {'Content-Type':'application/directory'}
347 f7ab99df Sofia Papagiannaki
        self.create_object(container, object, f=None, headers=h)
348 f7ab99df Sofia Papagiannaki
    
349 f89e3cf4 Sofia Papagiannaki
    def _set_public(self, headers, public=False):
350 f89e3cf4 Sofia Papagiannaki
        """
351 f89e3cf4 Sofia Papagiannaki
        sets the public header
352 f89e3cf4 Sofia Papagiannaki
        """
353 f89e3cf4 Sofia Papagiannaki
        if public == None:
354 f89e3cf4 Sofia Papagiannaki
            return
355 f89e3cf4 Sofia Papagiannaki
        elif public:
356 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Public'] = public
357 f89e3cf4 Sofia Papagiannaki
        else:
358 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Public'] = ''
359 f89e3cf4 Sofia Papagiannaki
    
360 ec1b8d3e Sofia Papagiannaki
    def create_object(self, container, object, f=stdin, chunked=False,
361 f89e3cf4 Sofia Papagiannaki
                      blocksize=1024, headers={}, use_hashes=False,
362 f89e3cf4 Sofia Papagiannaki
                      public=None, **meta):
363 d2d5c360 Sofia Papagiannaki
        """
364 d2d5c360 Sofia Papagiannaki
        creates an object
365 d2d5c360 Sofia Papagiannaki
        if f is None then creates a zero length object
366 d2d5c360 Sofia Papagiannaki
        if f is stdin or chunked is set then performs chunked transfer 
367 d2d5c360 Sofia Papagiannaki
        """
368 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
369 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
370 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
371 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
372 f89e3cf4 Sofia Papagiannaki
        headers = headers if headers else None
373 bcb7c5a8 Sofia Papagiannaki
        if not chunked:
374 bcb7c5a8 Sofia Papagiannaki
            format = 'json' if use_hashes else 'text'
375 f7ab99df Sofia Papagiannaki
            data = f.read() if f else None
376 bcb7c5a8 Sofia Papagiannaki
            if data:
377 bcb7c5a8 Sofia Papagiannaki
                if format == 'json':
378 bcb7c5a8 Sofia Papagiannaki
                    data = eval(data)
379 bcb7c5a8 Sofia Papagiannaki
                    data = json.dumps(data)
380 bcb7c5a8 Sofia Papagiannaki
            return self.put(path, data, headers=headers, format=format)
381 ec1b8d3e Sofia Papagiannaki
        else:
382 e3fd7f91 Sofia Papagiannaki
            return self._chunked_transfer(path, 'PUT', f, headers=headers,
383 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
384 f7ab99df Sofia Papagiannaki
    
385 ec1b8d3e Sofia Papagiannaki
    def update_object(self, container, object, f=stdin, chunked=False,
386 f89e3cf4 Sofia Papagiannaki
                      blocksize=1024, headers={}, offset=None, public=None,
387 f89e3cf4 Sofia Papagiannaki
                      **meta):
388 f89e3cf4 Sofia Papagiannaki
        print locals()
389 ec1b8d3e Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
390 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
391 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
392 bcb7c5a8 Sofia Papagiannaki
        if offset:
393 bcb7c5a8 Sofia Papagiannaki
            headers['Content-Range'] = 'bytes %s-/*' % offset
394 bcb7c5a8 Sofia Papagiannaki
        else:
395 bcb7c5a8 Sofia Papagiannaki
            headers['Content-Range'] = 'bytes */*'
396 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
397 f89e3cf4 Sofia Papagiannaki
        headers = headers if headers else None
398 ec1b8d3e Sofia Papagiannaki
        if not chunked and f != stdin:
399 a2defd86 Sofia Papagiannaki
            data = f.read() if f else None
400 ec1b8d3e Sofia Papagiannaki
            self.post(path, data, headers=headers)
401 ec1b8d3e Sofia Papagiannaki
        else:
402 ec1b8d3e Sofia Papagiannaki
            self._chunked_transfer(path, 'POST', f, headers=headers,
403 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
404 f7ab99df Sofia Papagiannaki
    
405 ec1b8d3e Sofia Papagiannaki
    def _change_obj_location(self, src_container, src_object, dst_container,
406 f89e3cf4 Sofia Papagiannaki
                             dst_object, remove=False, public=None, headers={}):
407 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (dst_container, dst_object)
408 ad71a0ce Sofia Papagiannaki
        if not headers:
409 ad71a0ce Sofia Papagiannaki
            headers = {}
410 ec1b8d3e Sofia Papagiannaki
        if remove:
411 ec1b8d3e Sofia Papagiannaki
            headers['X-Move-From'] = '/%s/%s' % (src_container, src_object)
412 ec1b8d3e Sofia Papagiannaki
        else:
413 ec1b8d3e Sofia Papagiannaki
            headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
414 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
415 f89e3cf4 Sofia Papagiannaki
        self.headers = headers if headers else None
416 3f5b02c7 Sofia Papagiannaki
        headers['Content-Length'] = 0
417 3f5b02c7 Sofia Papagiannaki
        self.put(path, headers=headers)
418 f7ab99df Sofia Papagiannaki
    
419 ec1b8d3e Sofia Papagiannaki
    def copy_object(self, src_container, src_object, dst_container,
420 f89e3cf4 Sofia Papagiannaki
                             dst_object, public=False, headers=None):
421 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
422 7e999dec Sofia Papagiannaki
                                   dst_container, dst_object,
423 f89e3cf4 Sofia Papagiannaki
                                   public, headers)
424 f7ab99df Sofia Papagiannaki
    
425 ec1b8d3e Sofia Papagiannaki
    def move_object(self, src_container, src_object, dst_container,
426 ad71a0ce Sofia Papagiannaki
                             dst_object, headers=None):
427 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
428 f89e3cf4 Sofia Papagiannaki
                                   dst_container, dst_object, True,
429 f89e3cf4 Sofia Papagiannaki
                                   public, headers)
430 f7ab99df Sofia Papagiannaki
    
431 3f5b02c7 Sofia Papagiannaki
    def delete_object(self, container, object):
432 3f5b02c7 Sofia Papagiannaki
        self.delete('/%s/%s' % (container, object))
433 f7ab99df Sofia Papagiannaki
    
434 8fe01d72 Sofia Papagiannaki
    def retrieve_object_metadata(self, container, object, restricted=False,
435 8fe01d72 Sofia Papagiannaki
                                 version=None):
436 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
437 f7ab99df Sofia Papagiannaki
        prefix = 'x-object-meta-' if restricted else None
438 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None
439 8fe01d72 Sofia Papagiannaki
        return self._get_metadata(path, prefix, params=params)
440 f7ab99df Sofia Papagiannaki
    
441 3f5b02c7 Sofia Papagiannaki
    def update_object_metadata(self, container, object, **meta):
442 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
443 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
444 f7ab99df Sofia Papagiannaki
    
445 f7ab99df Sofia Papagiannaki
    def delete_object_metadata(self, container, object, meta=[]):
446 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
447 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'object', meta)
448 f7ab99df Sofia Papagiannaki
    
449 f7ab99df Sofia Papagiannaki
    def trash_object(self, container, object):
450 f7ab99df Sofia Papagiannaki
        """
451 f7ab99df Sofia Papagiannaki
        trashes an object
452 f7ab99df Sofia Papagiannaki
        actually resets all object metadata with trash = true 
453 f7ab99df Sofia Papagiannaki
        """
454 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
455 f7ab99df Sofia Papagiannaki
        meta = {'trash':'true'}
456 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
457 f7ab99df Sofia Papagiannaki
    
458 f7ab99df Sofia Papagiannaki
    def restore_object(self, container, object):
459 f7ab99df Sofia Papagiannaki
        """
460 f7ab99df Sofia Papagiannaki
        restores a trashed object
461 961f2fbe Sofia Papagiannaki
        actualy removes trash object metadata info
462 f7ab99df Sofia Papagiannaki
        """
463 f7ab99df Sofia Papagiannaki
        self.delete_object_metadata(container, object, ['trash'])
464 f89e3cf4 Sofia Papagiannaki
    
465 f89e3cf4 Sofia Papagiannaki
    def publish_object(self, container, object):
466 f89e3cf4 Sofia Papagiannaki
        """
467 f89e3cf4 Sofia Papagiannaki
        sets a previously created object publicly accessible
468 f89e3cf4 Sofia Papagiannaki
        """
469 f89e3cf4 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
470 f89e3cf4 Sofia Papagiannaki
        headers = {}
471 f89e3cf4 Sofia Papagiannaki
        headers['Content-Range'] = 'bytes */*'
472 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public=True)
473 f89e3cf4 Sofia Papagiannaki
        self.post(path, headers=headers)
474 f89e3cf4 Sofia Papagiannaki
    
475 f89e3cf4 Sofia Papagiannaki
    def unpublish_object(self, container, object):
476 f89e3cf4 Sofia Papagiannaki
        """
477 f89e3cf4 Sofia Papagiannaki
        unpublish an object
478 f89e3cf4 Sofia Papagiannaki
        """
479 f89e3cf4 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
480 f89e3cf4 Sofia Papagiannaki
        headers = {}
481 f89e3cf4 Sofia Papagiannaki
        headers['Content-Range'] = 'bytes */*'
482 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public=False)
483 f89e3cf4 Sofia Papagiannaki
        self.post(path, headers=headers)