Statistics
| Branch: | Tag: | Revision:

root / pithos / lib / client.py @ 4013a851

History | View | Annotate | Download (17.7 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 3f5b02c7 Sofia Papagiannaki
        conn = HTTPConnection(self.host)
140 3f5b02c7 Sofia Papagiannaki
        
141 d2d5c360 Sofia Papagiannaki
        #encode whitespace
142 d2d5c360 Sofia Papagiannaki
        full_path = full_path.replace(' ', '%20')
143 ec1b8d3e Sofia Papagiannaki
        
144 3f5b02c7 Sofia Papagiannaki
        kwargs = {}
145 3f5b02c7 Sofia Papagiannaki
        kwargs['headers'] = headers or {}
146 bcb7c5a8 Sofia Papagiannaki
        kwargs['headers']['X-Auth-Token'] = self.token
147 ec1b8d3e Sofia Papagiannaki
        if not headers or \
148 ec1b8d3e Sofia Papagiannaki
        'Transfer-Encoding' not in headers \
149 ec1b8d3e Sofia Papagiannaki
        or headers['Transfer-Encoding'] != 'chunked':
150 ec1b8d3e Sofia Papagiannaki
            kwargs['headers']['Content-Length'] = len(body) if body else 0
151 3f5b02c7 Sofia Papagiannaki
        if body:
152 3f5b02c7 Sofia Papagiannaki
            kwargs['body'] = body
153 4013a851 Sofia Papagiannaki
        else:
154 4013a851 Sofia Papagiannaki
            kwargs['headers']['Content-Type'] = ''
155 bcb7c5a8 Sofia Papagiannaki
        kwargs['headers'].setdefault('Content-Type', 'application/octet-stream')
156 e3fd7f91 Sofia Papagiannaki
        try:
157 e3fd7f91 Sofia Papagiannaki
            conn.request(method, full_path, **kwargs)
158 e3fd7f91 Sofia Papagiannaki
        except socket.error, e:
159 e3fd7f91 Sofia Papagiannaki
            raise Fault(status=503)
160 e3fd7f91 Sofia Papagiannaki
            
161 3f5b02c7 Sofia Papagiannaki
        resp = conn.getresponse()
162 3f5b02c7 Sofia Papagiannaki
        headers = dict(resp.getheaders())
163 3f5b02c7 Sofia Papagiannaki
        
164 3f5b02c7 Sofia Papagiannaki
        if self.verbose:
165 3f5b02c7 Sofia Papagiannaki
            print '%d %s' % (resp.status, resp.reason)
166 3f5b02c7 Sofia Papagiannaki
            for key, val in headers.items():
167 3f5b02c7 Sofia Papagiannaki
                print '%s: %s' % (key.capitalize(), val)
168 3f5b02c7 Sofia Papagiannaki
            print
169 3f5b02c7 Sofia Papagiannaki
        
170 f7ab99df Sofia Papagiannaki
        length = resp.getheader('Content-length', None)
171 6cd1831a Sofia Papagiannaki
        data = resp.read(length)
172 3f5b02c7 Sofia Papagiannaki
        if self.debug:
173 3f5b02c7 Sofia Papagiannaki
            print data
174 3f5b02c7 Sofia Papagiannaki
            print
175 3f5b02c7 Sofia Papagiannaki
        
176 e3fd7f91 Sofia Papagiannaki
        if int(resp.status) in ERROR_CODES.keys():
177 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(resp.status))
178 e3fd7f91 Sofia Papagiannaki
        
179 e3fd7f91 Sofia Papagiannaki
        #print '*',  resp.status, headers, data
180 3f5b02c7 Sofia Papagiannaki
        return resp.status, headers, data
181 f7ab99df Sofia Papagiannaki
    
182 3f5b02c7 Sofia Papagiannaki
    def delete(self, path, format='text'):
183 3f5b02c7 Sofia Papagiannaki
        return self.req('DELETE', path, format=format)
184 f7ab99df Sofia Papagiannaki
    
185 3f5b02c7 Sofia Papagiannaki
    def get(self, path, format='text', headers=None, params=None):
186 3f5b02c7 Sofia Papagiannaki
        return self.req('GET', path, headers=headers, format=format,
187 ec1b8d3e Sofia Papagiannaki
                        params=params)
188 f7ab99df Sofia Papagiannaki
    
189 d2d5c360 Sofia Papagiannaki
    def head(self, path, format='text', params=None):
190 d2d5c360 Sofia Papagiannaki
        return self.req('HEAD', path, format=format, params=params)
191 f7ab99df Sofia Papagiannaki
    
192 3f5b02c7 Sofia Papagiannaki
    def post(self, path, body=None, format='text', headers=None):
193 3f5b02c7 Sofia Papagiannaki
        return self.req('POST', path, body, headers=headers, format=format)
194 f7ab99df Sofia Papagiannaki
    
195 3f5b02c7 Sofia Papagiannaki
    def put(self, path, body=None, format='text', headers=None):
196 3f5b02c7 Sofia Papagiannaki
        return self.req('PUT', path, body, headers=headers, format=format)
197 f7ab99df Sofia Papagiannaki
    
198 3f5b02c7 Sofia Papagiannaki
    def _list(self, path, detail=False, params=None, headers=None):
199 3f5b02c7 Sofia Papagiannaki
        format = 'json' if detail else 'text'
200 3f5b02c7 Sofia Papagiannaki
        status, headers, data = self.get(path, format=format, headers=headers,
201 3f5b02c7 Sofia Papagiannaki
                                         params=params)
202 ec1b8d3e Sofia Papagiannaki
        if detail:
203 bcb7c5a8 Sofia Papagiannaki
            data = json.loads(data) if data else ''
204 ec1b8d3e Sofia Papagiannaki
        else:
205 b41a95a0 Sofia Papagiannaki
            data = data.strip().split('\n')
206 3f5b02c7 Sofia Papagiannaki
        return data
207 f7ab99df Sofia Papagiannaki
    
208 d2d5c360 Sofia Papagiannaki
    def _get_metadata(self, path, prefix=None, params=None):
209 d2d5c360 Sofia Papagiannaki
        status, headers, data = self.head(path, params=params)
210 f7ab99df Sofia Papagiannaki
        prefixlen = len(prefix) if prefix else 0
211 3f5b02c7 Sofia Papagiannaki
        meta = {}
212 3f5b02c7 Sofia Papagiannaki
        for key, val in headers.items():
213 0ea1dcc4 Sofia Papagiannaki
            if prefix and not key.startswith(prefix):
214 0ea1dcc4 Sofia Papagiannaki
                continue
215 0ea1dcc4 Sofia Papagiannaki
            elif prefix and key.startswith(prefix):
216 3f5b02c7 Sofia Papagiannaki
                key = key[prefixlen:]
217 0ea1dcc4 Sofia Papagiannaki
            meta[key] = val
218 3f5b02c7 Sofia Papagiannaki
        return meta
219 f7ab99df Sofia Papagiannaki
    
220 f7ab99df Sofia Papagiannaki
    def _update_metadata(self, path, entity, **meta):
221 f7ab99df Sofia Papagiannaki
        """
222 f7ab99df Sofia Papagiannaki
         adds new and updates the values of previously set metadata
223 f7ab99df Sofia Papagiannaki
        """
224 961f2fbe Sofia Papagiannaki
        prefix = 'x-%s-meta-' % entity
225 961f2fbe Sofia Papagiannaki
        prev_meta = self._get_metadata(path, prefix)
226 f7ab99df Sofia Papagiannaki
        prev_meta.update(meta)
227 f7ab99df Sofia Papagiannaki
        headers = {}
228 f7ab99df Sofia Papagiannaki
        for key, val in prev_meta.items():
229 961f2fbe Sofia Papagiannaki
            key = '%s%s' % (prefix, key)
230 961f2fbe Sofia Papagiannaki
            key = '-'.join(elem.capitalize() for elem in key.split('-'))
231 961f2fbe Sofia Papagiannaki
            headers[key] = val
232 f7ab99df Sofia Papagiannaki
        self.post(path, headers=headers)
233 f7ab99df Sofia Papagiannaki
    
234 f7ab99df Sofia Papagiannaki
    def _delete_metadata(self, path, entity, meta=[]):
235 f7ab99df Sofia Papagiannaki
        """
236 f7ab99df Sofia Papagiannaki
        delete previously set metadata
237 f7ab99df Sofia Papagiannaki
        """
238 961f2fbe Sofia Papagiannaki
        prefix = 'x-%s-meta-' % entity
239 961f2fbe Sofia Papagiannaki
        prev_meta = self._get_metadata(path, prefix)
240 f7ab99df Sofia Papagiannaki
        headers = {}
241 f7ab99df Sofia Papagiannaki
        for key, val in prev_meta.items():
242 961f2fbe Sofia Papagiannaki
            if key in meta:
243 f7ab99df Sofia Papagiannaki
                continue
244 961f2fbe Sofia Papagiannaki
            key = '%s%s' % (prefix, key)
245 961f2fbe Sofia Papagiannaki
            key = '-'.join(elem.capitalize() for elem in key.split('-'))
246 961f2fbe Sofia Papagiannaki
            headers[key] = val
247 3f5b02c7 Sofia Papagiannaki
        self.post(path, headers=headers)
248 f7ab99df Sofia Papagiannaki
    
249 3f5b02c7 Sofia Papagiannaki
    # Storage Account Services
250 f7ab99df Sofia Papagiannaki
    
251 ec1b8d3e Sofia Papagiannaki
    def list_containers(self, detail=False, params=None, headers=None):
252 3f5b02c7 Sofia Papagiannaki
        return self._list('', detail, params, headers)
253 f7ab99df Sofia Papagiannaki
    
254 d2d5c360 Sofia Papagiannaki
    def account_metadata(self, restricted=False, until=None):
255 f7ab99df Sofia Papagiannaki
        prefix = 'x-account-meta-' if restricted else None
256 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
257 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('', prefix, params=params)
258 f7ab99df Sofia Papagiannaki
    
259 3f5b02c7 Sofia Papagiannaki
    def update_account_metadata(self, **meta):
260 f7ab99df Sofia Papagiannaki
        self._update_metadata('', 'account', **meta)
261 f7ab99df Sofia Papagiannaki
        
262 f7ab99df Sofia Papagiannaki
    def delete_account_metadata(self, meta=[]):
263 f7ab99df Sofia Papagiannaki
        self._delete_metadata('', 'account', meta)
264 f7ab99df Sofia Papagiannaki
    
265 bcb7c5a8 Sofia Papagiannaki
    def set_account_groups(self, **groups):
266 d3fd269f Sofia Papagiannaki
        headers = {}
267 d3fd269f Sofia Papagiannaki
        for key, val in groups.items():
268 d3fd269f Sofia Papagiannaki
            headers['X-Account-Group-%s' % key.capitalize()] = val
269 d3fd269f Sofia Papagiannaki
        self.post('', headers=headers)
270 d3fd269f Sofia Papagiannaki
    
271 3f5b02c7 Sofia Papagiannaki
    # Storage Container Services
272 f7ab99df Sofia Papagiannaki
    
273 a4c10cbc Sofia Papagiannaki
    def _filter(self, l, d):
274 a4c10cbc Sofia Papagiannaki
        """
275 a4c10cbc Sofia Papagiannaki
        filter out from l elements having the metadata values provided
276 a4c10cbc Sofia Papagiannaki
        """
277 a4c10cbc Sofia Papagiannaki
        ll = l
278 a4c10cbc Sofia Papagiannaki
        for elem in l:
279 a4c10cbc Sofia Papagiannaki
            if type(elem) == types.DictionaryType:
280 a4c10cbc Sofia Papagiannaki
                for key in d.keys():
281 a4c10cbc Sofia Papagiannaki
                    k = 'x_object_meta_%s' % key
282 a4c10cbc Sofia Papagiannaki
                    if k in elem.keys() and elem[k] == d[key]:
283 a4c10cbc Sofia Papagiannaki
                        ll.remove(elem)
284 a4c10cbc Sofia Papagiannaki
                        break
285 a4c10cbc Sofia Papagiannaki
        return ll
286 a4c10cbc Sofia Papagiannaki
    
287 a4c10cbc Sofia Papagiannaki
    def _filter_trashed(self, l):
288 a4c10cbc Sofia Papagiannaki
        return self._filter(l, {'trash':'true'})
289 a4c10cbc Sofia Papagiannaki
    
290 bcb7c5a8 Sofia Papagiannaki
    def list_objects(self, container, detail=False, headers=None,
291 bcb7c5a8 Sofia Papagiannaki
                     include_trashed=False, **params):
292 a4c10cbc Sofia Papagiannaki
        l = self._list('/' + container, detail, params, headers)
293 a4c10cbc Sofia Papagiannaki
        if not include_trashed:
294 a4c10cbc Sofia Papagiannaki
            l = self._filter_trashed(l)
295 a4c10cbc Sofia Papagiannaki
        return l
296 f7ab99df Sofia Papagiannaki
    
297 bcb7c5a8 Sofia Papagiannaki
    def create_container(self, container, headers=None, **meta):
298 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
299 f89e3cf4 Sofia Papagiannaki
            headers['X-Container-Meta-%s' %k.strip().upper()] = v.strip()
300 ec1b8d3e Sofia Papagiannaki
        status, header, data = self.put('/' + container, headers=headers)
301 3f5b02c7 Sofia Papagiannaki
        if status == 202:
302 3f5b02c7 Sofia Papagiannaki
            return False
303 3f5b02c7 Sofia Papagiannaki
        elif status != 201:
304 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(status))
305 3f5b02c7 Sofia Papagiannaki
        return True
306 f7ab99df Sofia Papagiannaki
    
307 3f5b02c7 Sofia Papagiannaki
    def delete_container(self, container):
308 3f5b02c7 Sofia Papagiannaki
        self.delete('/' + container)
309 f7ab99df Sofia Papagiannaki
    
310 d2d5c360 Sofia Papagiannaki
    def retrieve_container_metadata(self, container, restricted=False,
311 d2d5c360 Sofia Papagiannaki
                                    until=None):
312 f7ab99df Sofia Papagiannaki
        prefix = 'x-container-meta-' if restricted else None
313 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
314 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('/%s' % container, prefix, params=params)
315 f7ab99df Sofia Papagiannaki
    
316 3f5b02c7 Sofia Papagiannaki
    def update_container_metadata(self, container, **meta):
317 f7ab99df Sofia Papagiannaki
        self._update_metadata('/' + container, 'container', **meta)
318 f7ab99df Sofia Papagiannaki
        
319 f7ab99df Sofia Papagiannaki
    def delete_container_metadata(self, container, meta=[]):
320 f7ab99df Sofia Papagiannaki
        path = '/%s' % (container)
321 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'container', meta)
322 f7ab99df Sofia Papagiannaki
    
323 bcb7c5a8 Sofia Papagiannaki
    def set_container_policies(self, container, **policies):
324 bcb7c5a8 Sofia Papagiannaki
        path = '/%s' % (container)
325 bcb7c5a8 Sofia Papagiannaki
        headers = {}
326 bcb7c5a8 Sofia Papagiannaki
        print ''
327 bcb7c5a8 Sofia Papagiannaki
        for key, val in policies.items():
328 bcb7c5a8 Sofia Papagiannaki
            headers['X-Container-Policy-%s' % key.capitalize()] = val
329 bcb7c5a8 Sofia Papagiannaki
        self.post(path, headers=headers)
330 bcb7c5a8 Sofia Papagiannaki
    
331 3f5b02c7 Sofia Papagiannaki
    # Storage Object Services
332 f7ab99df Sofia Papagiannaki
    
333 8fe01d72 Sofia Papagiannaki
    def retrieve_object(self, container, object, detail=False, headers=None,
334 8fe01d72 Sofia Papagiannaki
                        version=None):
335 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
336 ec1b8d3e Sofia Papagiannaki
        format = 'json' if detail else 'text'
337 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None 
338 8fe01d72 Sofia Papagiannaki
        status, headers, data = self.get(path, format, headers, params)
339 3f5b02c7 Sofia Papagiannaki
        return data
340 f7ab99df Sofia Papagiannaki
    
341 f7ab99df Sofia Papagiannaki
    def create_directory_marker(self, container, object):
342 f7ab99df Sofia Papagiannaki
        if not object:
343 f7ab99df Sofia Papagiannaki
            raise Fault('Directory markers have to be nested in a container')
344 f7ab99df Sofia Papagiannaki
        h = {'Content-Type':'application/directory'}
345 f7ab99df Sofia Papagiannaki
        self.create_object(container, object, f=None, headers=h)
346 f7ab99df Sofia Papagiannaki
    
347 f89e3cf4 Sofia Papagiannaki
    def _set_public(self, headers, public=False):
348 f89e3cf4 Sofia Papagiannaki
        """
349 f89e3cf4 Sofia Papagiannaki
        sets the public header
350 f89e3cf4 Sofia Papagiannaki
        """
351 f89e3cf4 Sofia Papagiannaki
        if public == None:
352 f89e3cf4 Sofia Papagiannaki
            return
353 f89e3cf4 Sofia Papagiannaki
        elif public:
354 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Public'] = public
355 f89e3cf4 Sofia Papagiannaki
        else:
356 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Public'] = ''
357 f89e3cf4 Sofia Papagiannaki
    
358 ec1b8d3e Sofia Papagiannaki
    def create_object(self, container, object, f=stdin, chunked=False,
359 f89e3cf4 Sofia Papagiannaki
                      blocksize=1024, headers={}, use_hashes=False,
360 f89e3cf4 Sofia Papagiannaki
                      public=None, **meta):
361 d2d5c360 Sofia Papagiannaki
        """
362 d2d5c360 Sofia Papagiannaki
        creates an object
363 d2d5c360 Sofia Papagiannaki
        if f is None then creates a zero length object
364 d2d5c360 Sofia Papagiannaki
        if f is stdin or chunked is set then performs chunked transfer 
365 d2d5c360 Sofia Papagiannaki
        """
366 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
367 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
368 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
369 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
370 f89e3cf4 Sofia Papagiannaki
        headers = headers if headers else None
371 bcb7c5a8 Sofia Papagiannaki
        if not chunked:
372 bcb7c5a8 Sofia Papagiannaki
            format = 'json' if use_hashes else 'text'
373 f7ab99df Sofia Papagiannaki
            data = f.read() if f else None
374 bcb7c5a8 Sofia Papagiannaki
            if data:
375 bcb7c5a8 Sofia Papagiannaki
                if format == 'json':
376 bcb7c5a8 Sofia Papagiannaki
                    data = eval(data)
377 bcb7c5a8 Sofia Papagiannaki
                    data = json.dumps(data)
378 bcb7c5a8 Sofia Papagiannaki
            return self.put(path, data, headers=headers, format=format)
379 ec1b8d3e Sofia Papagiannaki
        else:
380 e3fd7f91 Sofia Papagiannaki
            return self._chunked_transfer(path, 'PUT', f, headers=headers,
381 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
382 f7ab99df Sofia Papagiannaki
    
383 ec1b8d3e Sofia Papagiannaki
    def update_object(self, container, object, f=stdin, chunked=False,
384 f89e3cf4 Sofia Papagiannaki
                      blocksize=1024, headers={}, offset=None, public=None,
385 f89e3cf4 Sofia Papagiannaki
                      **meta):
386 f89e3cf4 Sofia Papagiannaki
        print locals()
387 ec1b8d3e Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
388 bcb7c5a8 Sofia Papagiannaki
        for k,v in meta.items():
389 f89e3cf4 Sofia Papagiannaki
            headers['X-Object-Meta-%s' %k.strip().upper()] = v.strip()
390 bcb7c5a8 Sofia Papagiannaki
        if offset:
391 bcb7c5a8 Sofia Papagiannaki
            headers['Content-Range'] = 'bytes %s-/*' % offset
392 bcb7c5a8 Sofia Papagiannaki
        else:
393 bcb7c5a8 Sofia Papagiannaki
            headers['Content-Range'] = 'bytes */*'
394 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
395 f89e3cf4 Sofia Papagiannaki
        headers = headers if headers else None
396 ec1b8d3e Sofia Papagiannaki
        if not chunked and f != stdin:
397 a2defd86 Sofia Papagiannaki
            data = f.read() if f else None
398 ec1b8d3e Sofia Papagiannaki
            self.post(path, data, headers=headers)
399 ec1b8d3e Sofia Papagiannaki
        else:
400 ec1b8d3e Sofia Papagiannaki
            self._chunked_transfer(path, 'POST', f, headers=headers,
401 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
402 f7ab99df Sofia Papagiannaki
    
403 ec1b8d3e Sofia Papagiannaki
    def _change_obj_location(self, src_container, src_object, dst_container,
404 f89e3cf4 Sofia Papagiannaki
                             dst_object, remove=False, public=None, headers={}):
405 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (dst_container, dst_object)
406 ad71a0ce Sofia Papagiannaki
        if not headers:
407 ad71a0ce Sofia Papagiannaki
            headers = {}
408 ec1b8d3e Sofia Papagiannaki
        if remove:
409 ec1b8d3e Sofia Papagiannaki
            headers['X-Move-From'] = '/%s/%s' % (src_container, src_object)
410 ec1b8d3e Sofia Papagiannaki
        else:
411 ec1b8d3e Sofia Papagiannaki
            headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
412 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public)
413 f89e3cf4 Sofia Papagiannaki
        self.headers = headers if headers else None
414 3f5b02c7 Sofia Papagiannaki
        headers['Content-Length'] = 0
415 3f5b02c7 Sofia Papagiannaki
        self.put(path, headers=headers)
416 f7ab99df Sofia Papagiannaki
    
417 ec1b8d3e Sofia Papagiannaki
    def copy_object(self, src_container, src_object, dst_container,
418 f89e3cf4 Sofia Papagiannaki
                             dst_object, public=False, headers=None):
419 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
420 7e999dec Sofia Papagiannaki
                                   dst_container, dst_object,
421 f89e3cf4 Sofia Papagiannaki
                                   public, headers)
422 f7ab99df Sofia Papagiannaki
    
423 ec1b8d3e Sofia Papagiannaki
    def move_object(self, src_container, src_object, dst_container,
424 ad71a0ce Sofia Papagiannaki
                             dst_object, headers=None):
425 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
426 f89e3cf4 Sofia Papagiannaki
                                   dst_container, dst_object, True,
427 f89e3cf4 Sofia Papagiannaki
                                   public, headers)
428 f7ab99df Sofia Papagiannaki
    
429 3f5b02c7 Sofia Papagiannaki
    def delete_object(self, container, object):
430 3f5b02c7 Sofia Papagiannaki
        self.delete('/%s/%s' % (container, object))
431 f7ab99df Sofia Papagiannaki
    
432 8fe01d72 Sofia Papagiannaki
    def retrieve_object_metadata(self, container, object, restricted=False,
433 8fe01d72 Sofia Papagiannaki
                                 version=None):
434 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
435 f7ab99df Sofia Papagiannaki
        prefix = 'x-object-meta-' if restricted else None
436 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None
437 8fe01d72 Sofia Papagiannaki
        return self._get_metadata(path, prefix, params=params)
438 f7ab99df Sofia Papagiannaki
    
439 3f5b02c7 Sofia Papagiannaki
    def update_object_metadata(self, container, object, **meta):
440 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
441 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
442 f7ab99df Sofia Papagiannaki
    
443 f7ab99df Sofia Papagiannaki
    def delete_object_metadata(self, container, object, meta=[]):
444 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
445 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'object', meta)
446 f7ab99df Sofia Papagiannaki
    
447 f7ab99df Sofia Papagiannaki
    def trash_object(self, container, object):
448 f7ab99df Sofia Papagiannaki
        """
449 f7ab99df Sofia Papagiannaki
        trashes an object
450 f7ab99df Sofia Papagiannaki
        actually resets all object metadata with trash = true 
451 f7ab99df Sofia Papagiannaki
        """
452 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
453 f7ab99df Sofia Papagiannaki
        meta = {'trash':'true'}
454 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
455 f7ab99df Sofia Papagiannaki
    
456 f7ab99df Sofia Papagiannaki
    def restore_object(self, container, object):
457 f7ab99df Sofia Papagiannaki
        """
458 f7ab99df Sofia Papagiannaki
        restores a trashed object
459 961f2fbe Sofia Papagiannaki
        actualy removes trash object metadata info
460 f7ab99df Sofia Papagiannaki
        """
461 f7ab99df Sofia Papagiannaki
        self.delete_object_metadata(container, object, ['trash'])
462 f89e3cf4 Sofia Papagiannaki
    
463 f89e3cf4 Sofia Papagiannaki
    def publish_object(self, container, object):
464 f89e3cf4 Sofia Papagiannaki
        """
465 f89e3cf4 Sofia Papagiannaki
        sets a previously created object publicly accessible
466 f89e3cf4 Sofia Papagiannaki
        """
467 f89e3cf4 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
468 f89e3cf4 Sofia Papagiannaki
        headers = {}
469 f89e3cf4 Sofia Papagiannaki
        headers['Content-Range'] = 'bytes */*'
470 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public=True)
471 f89e3cf4 Sofia Papagiannaki
        self.post(path, headers=headers)
472 f89e3cf4 Sofia Papagiannaki
    
473 f89e3cf4 Sofia Papagiannaki
    def unpublish_object(self, container, object):
474 f89e3cf4 Sofia Papagiannaki
        """
475 f89e3cf4 Sofia Papagiannaki
        unpublish an object
476 f89e3cf4 Sofia Papagiannaki
        """
477 f89e3cf4 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
478 f89e3cf4 Sofia Papagiannaki
        headers = {}
479 f89e3cf4 Sofia Papagiannaki
        headers['Content-Range'] = 'bytes */*'
480 f89e3cf4 Sofia Papagiannaki
        self._set_public(headers, public=False)
481 f89e3cf4 Sofia Papagiannaki
        self.post(path, headers=headers)