Statistics
| Branch: | Tag: | Revision:

root / pithos / lib / client.py @ f7ab99df

History | View | Annotate | Download (12.8 kB)

1 ec1b8d3e Sofia Papagiannaki
from httplib import HTTPConnection, HTTP
2 ec1b8d3e Sofia Papagiannaki
from sys import stdin
3 3f5b02c7 Sofia Papagiannaki
4 3f5b02c7 Sofia Papagiannaki
import json
5 ec1b8d3e Sofia Papagiannaki
import types
6 0ea1dcc4 Sofia Papagiannaki
import socket
7 e3fd7f91 Sofia Papagiannaki
import pithos.api.faults
8 e3fd7f91 Sofia Papagiannaki
9 e3fd7f91 Sofia Papagiannaki
ERROR_CODES = {304:'Not Modified',
10 e3fd7f91 Sofia Papagiannaki
               400:'Bad Request',
11 e3fd7f91 Sofia Papagiannaki
               401:'Unauthorized',
12 e3fd7f91 Sofia Papagiannaki
               404:'Not Found',
13 e3fd7f91 Sofia Papagiannaki
               409:'Conflict',
14 e3fd7f91 Sofia Papagiannaki
               411:'Length Required',
15 e3fd7f91 Sofia Papagiannaki
               412:'Precondition Failed',
16 e3fd7f91 Sofia Papagiannaki
               416:'Range Not Satisfiable',
17 e3fd7f91 Sofia Papagiannaki
               422:'Unprocessable Entity',
18 e3fd7f91 Sofia Papagiannaki
               503:'Service Unavailable'}
19 3f5b02c7 Sofia Papagiannaki
20 3f5b02c7 Sofia Papagiannaki
class Fault(Exception):
21 e3fd7f91 Sofia Papagiannaki
    def __init__(self, data='', status=None):
22 e3fd7f91 Sofia Papagiannaki
        if data == '' and status in ERROR_CODES.keys():
23 e3fd7f91 Sofia Papagiannaki
            data = ERROR_CODES[status]
24 3f5b02c7 Sofia Papagiannaki
        Exception.__init__(self, data)
25 3f5b02c7 Sofia Papagiannaki
        self.data = data
26 e3fd7f91 Sofia Papagiannaki
        self.status = status
27 3f5b02c7 Sofia Papagiannaki
28 3f5b02c7 Sofia Papagiannaki
class Client(object):
29 3f5b02c7 Sofia Papagiannaki
    def __init__(self, host, account, api='v1', verbose=False, debug=False):
30 3f5b02c7 Sofia Papagiannaki
        """`host` can also include a port, e.g '127.0.0.1:8000'."""
31 3f5b02c7 Sofia Papagiannaki
        
32 3f5b02c7 Sofia Papagiannaki
        self.host = host
33 3f5b02c7 Sofia Papagiannaki
        self.account = account
34 3f5b02c7 Sofia Papagiannaki
        self.api = api
35 3f5b02c7 Sofia Papagiannaki
        self.verbose = verbose or debug
36 3f5b02c7 Sofia Papagiannaki
        self.debug = debug
37 f7ab99df Sofia Papagiannaki
    
38 ec1b8d3e Sofia Papagiannaki
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
39 ec1b8d3e Sofia Papagiannaki
                          blocksize=1024):
40 ec1b8d3e Sofia Papagiannaki
        http = HTTPConnection(self.host)
41 ec1b8d3e Sofia Papagiannaki
        
42 ec1b8d3e Sofia Papagiannaki
        # write header
43 ec1b8d3e Sofia Papagiannaki
        path = '/%s/%s%s' % (self.api, self.account, path)
44 ec1b8d3e Sofia Papagiannaki
        http.putrequest(method, path)
45 ec1b8d3e Sofia Papagiannaki
        http.putheader('Content-Type', 'application/octet-stream')
46 ec1b8d3e Sofia Papagiannaki
        http.putheader('Transfer-Encoding', 'chunked')
47 ec1b8d3e Sofia Papagiannaki
        if headers:
48 ec1b8d3e Sofia Papagiannaki
            for header,value in headers.items():
49 ec1b8d3e Sofia Papagiannaki
                http.putheader(header, value)
50 ec1b8d3e Sofia Papagiannaki
        http.endheaders()
51 ec1b8d3e Sofia Papagiannaki
        
52 ec1b8d3e Sofia Papagiannaki
        # write body
53 ec1b8d3e Sofia Papagiannaki
        data = ''
54 ec1b8d3e Sofia Papagiannaki
        while True:
55 ec1b8d3e Sofia Papagiannaki
            if f.closed:
56 ec1b8d3e Sofia Papagiannaki
                break
57 ec1b8d3e Sofia Papagiannaki
            block = f.read(blocksize)
58 ec1b8d3e Sofia Papagiannaki
            if block == '':
59 ec1b8d3e Sofia Papagiannaki
                break
60 ec1b8d3e Sofia Papagiannaki
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
61 ec1b8d3e Sofia Papagiannaki
            try:
62 ec1b8d3e Sofia Papagiannaki
                http.send(data)
63 ec1b8d3e Sofia Papagiannaki
            except:
64 ec1b8d3e Sofia Papagiannaki
                #retry
65 ec1b8d3e Sofia Papagiannaki
                http.send(data)
66 ec1b8d3e Sofia Papagiannaki
        data = '0x0\r\n'
67 ec1b8d3e Sofia Papagiannaki
        try:
68 ec1b8d3e Sofia Papagiannaki
            http.send(data)
69 ec1b8d3e Sofia Papagiannaki
        except:
70 ec1b8d3e Sofia Papagiannaki
            #retry
71 ec1b8d3e Sofia Papagiannaki
            http.send(data)
72 ec1b8d3e Sofia Papagiannaki
        
73 ec1b8d3e Sofia Papagiannaki
        # get response
74 ec1b8d3e Sofia Papagiannaki
        resp = http.getresponse()
75 e3fd7f91 Sofia Papagiannaki
        
76 ec1b8d3e Sofia Papagiannaki
        headers = dict(resp.getheaders())
77 ec1b8d3e Sofia Papagiannaki
        
78 ec1b8d3e Sofia Papagiannaki
        if self.verbose:
79 ec1b8d3e Sofia Papagiannaki
            print '%d %s' % (resp.status, resp.reason)
80 ec1b8d3e Sofia Papagiannaki
            for key, val in headers.items():
81 ec1b8d3e Sofia Papagiannaki
                print '%s: %s' % (key.capitalize(), val)
82 ec1b8d3e Sofia Papagiannaki
            print
83 ec1b8d3e Sofia Papagiannaki
        
84 f7ab99df Sofia Papagiannaki
        length = resp.getheader('Content-length', None)
85 6cd1831a Sofia Papagiannaki
        data = resp.read(length)
86 ec1b8d3e Sofia Papagiannaki
        if self.debug:
87 ec1b8d3e Sofia Papagiannaki
            print data
88 ec1b8d3e Sofia Papagiannaki
            print
89 ec1b8d3e Sofia Papagiannaki
        
90 e3fd7f91 Sofia Papagiannaki
        if int(resp.status) in ERROR_CODES.keys():
91 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(resp.status))
92 e3fd7f91 Sofia Papagiannaki
        
93 f7ab99df Sofia Papagiannaki
        #print '*',  resp.status, headers, data
94 ec1b8d3e Sofia Papagiannaki
        return resp.status, headers, data
95 f7ab99df Sofia Papagiannaki
    
96 d2d5c360 Sofia Papagiannaki
    def req(self, method, path, body=None, headers=None, format='text',
97 d2d5c360 Sofia Papagiannaki
            params=None):
98 d2d5c360 Sofia Papagiannaki
        full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path,
99 d2d5c360 Sofia Papagiannaki
                                            format)
100 3f5b02c7 Sofia Papagiannaki
        if params:
101 3f5b02c7 Sofia Papagiannaki
            for k,v in params.items():
102 ec1b8d3e Sofia Papagiannaki
                if v:
103 ec1b8d3e Sofia Papagiannaki
                    full_path = '%s&%s=%s' %(full_path, k, v)
104 3f5b02c7 Sofia Papagiannaki
        conn = HTTPConnection(self.host)
105 3f5b02c7 Sofia Papagiannaki
        
106 d2d5c360 Sofia Papagiannaki
        #encode whitespace
107 d2d5c360 Sofia Papagiannaki
        full_path = full_path.replace(' ', '%20')
108 ec1b8d3e Sofia Papagiannaki
        
109 3f5b02c7 Sofia Papagiannaki
        kwargs = {}
110 3f5b02c7 Sofia Papagiannaki
        kwargs['headers'] = headers or {}
111 ec1b8d3e Sofia Papagiannaki
        if not headers or \
112 ec1b8d3e Sofia Papagiannaki
        'Transfer-Encoding' not in headers \
113 ec1b8d3e Sofia Papagiannaki
        or headers['Transfer-Encoding'] != 'chunked':
114 ec1b8d3e Sofia Papagiannaki
            kwargs['headers']['Content-Length'] = len(body) if body else 0
115 3f5b02c7 Sofia Papagiannaki
        if body:
116 3f5b02c7 Sofia Papagiannaki
            kwargs['body'] = body
117 3f5b02c7 Sofia Papagiannaki
            kwargs['headers']['Content-Type'] = 'application/octet-stream'
118 ec1b8d3e Sofia Papagiannaki
        #print '****', method, full_path, kwargs
119 e3fd7f91 Sofia Papagiannaki
        try:
120 e3fd7f91 Sofia Papagiannaki
            conn.request(method, full_path, **kwargs)
121 e3fd7f91 Sofia Papagiannaki
        except socket.error, e:
122 e3fd7f91 Sofia Papagiannaki
            raise Fault(status=503)
123 e3fd7f91 Sofia Papagiannaki
            
124 3f5b02c7 Sofia Papagiannaki
        resp = conn.getresponse()
125 3f5b02c7 Sofia Papagiannaki
        headers = dict(resp.getheaders())
126 3f5b02c7 Sofia Papagiannaki
        
127 3f5b02c7 Sofia Papagiannaki
        if self.verbose:
128 3f5b02c7 Sofia Papagiannaki
            print '%d %s' % (resp.status, resp.reason)
129 3f5b02c7 Sofia Papagiannaki
            for key, val in headers.items():
130 3f5b02c7 Sofia Papagiannaki
                print '%s: %s' % (key.capitalize(), val)
131 3f5b02c7 Sofia Papagiannaki
            print
132 3f5b02c7 Sofia Papagiannaki
        
133 f7ab99df Sofia Papagiannaki
        length = resp.getheader('Content-length', None)
134 6cd1831a Sofia Papagiannaki
        data = resp.read(length)
135 3f5b02c7 Sofia Papagiannaki
        if self.debug:
136 3f5b02c7 Sofia Papagiannaki
            print data
137 3f5b02c7 Sofia Papagiannaki
            print
138 3f5b02c7 Sofia Papagiannaki
        
139 e3fd7f91 Sofia Papagiannaki
        if int(resp.status) in ERROR_CODES.keys():
140 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(resp.status))
141 e3fd7f91 Sofia Papagiannaki
        
142 e3fd7f91 Sofia Papagiannaki
        #print '*',  resp.status, headers, data
143 3f5b02c7 Sofia Papagiannaki
        return resp.status, headers, data
144 f7ab99df Sofia Papagiannaki
    
145 3f5b02c7 Sofia Papagiannaki
    def delete(self, path, format='text'):
146 3f5b02c7 Sofia Papagiannaki
        return self.req('DELETE', path, format=format)
147 f7ab99df Sofia Papagiannaki
    
148 3f5b02c7 Sofia Papagiannaki
    def get(self, path, format='text', headers=None, params=None):
149 3f5b02c7 Sofia Papagiannaki
        return self.req('GET', path, headers=headers, format=format,
150 ec1b8d3e Sofia Papagiannaki
                        params=params)
151 f7ab99df Sofia Papagiannaki
    
152 d2d5c360 Sofia Papagiannaki
    def head(self, path, format='text', params=None):
153 d2d5c360 Sofia Papagiannaki
        return self.req('HEAD', path, format=format, params=params)
154 f7ab99df Sofia Papagiannaki
    
155 3f5b02c7 Sofia Papagiannaki
    def post(self, path, body=None, format='text', headers=None):
156 3f5b02c7 Sofia Papagiannaki
        return self.req('POST', path, body, headers=headers, format=format)
157 f7ab99df Sofia Papagiannaki
    
158 3f5b02c7 Sofia Papagiannaki
    def put(self, path, body=None, format='text', headers=None):
159 3f5b02c7 Sofia Papagiannaki
        return self.req('PUT', path, body, headers=headers, format=format)
160 f7ab99df Sofia Papagiannaki
    
161 3f5b02c7 Sofia Papagiannaki
    def _list(self, path, detail=False, params=None, headers=None):
162 3f5b02c7 Sofia Papagiannaki
        format = 'json' if detail else 'text'
163 3f5b02c7 Sofia Papagiannaki
        status, headers, data = self.get(path, format=format, headers=headers,
164 3f5b02c7 Sofia Papagiannaki
                                         params=params)
165 ec1b8d3e Sofia Papagiannaki
        if detail:
166 ec1b8d3e Sofia Papagiannaki
            data = json.loads(data)
167 ec1b8d3e Sofia Papagiannaki
        else:
168 b41a95a0 Sofia Papagiannaki
            data = data.strip().split('\n')
169 3f5b02c7 Sofia Papagiannaki
        return data
170 f7ab99df Sofia Papagiannaki
    
171 d2d5c360 Sofia Papagiannaki
    def _get_metadata(self, path, prefix=None, params=None):
172 d2d5c360 Sofia Papagiannaki
        status, headers, data = self.head(path, params=params)
173 f7ab99df Sofia Papagiannaki
        prefixlen = len(prefix) if prefix else 0
174 3f5b02c7 Sofia Papagiannaki
        meta = {}
175 3f5b02c7 Sofia Papagiannaki
        for key, val in headers.items():
176 0ea1dcc4 Sofia Papagiannaki
            if prefix and not key.startswith(prefix):
177 0ea1dcc4 Sofia Papagiannaki
                continue
178 0ea1dcc4 Sofia Papagiannaki
            elif prefix and key.startswith(prefix):
179 3f5b02c7 Sofia Papagiannaki
                key = key[prefixlen:]
180 0ea1dcc4 Sofia Papagiannaki
            meta[key] = val
181 3f5b02c7 Sofia Papagiannaki
        return meta
182 f7ab99df Sofia Papagiannaki
    
183 f7ab99df Sofia Papagiannaki
    def _update_metadata(self, path, entity, **meta):
184 f7ab99df Sofia Papagiannaki
        """
185 f7ab99df Sofia Papagiannaki
         adds new and updates the values of previously set metadata
186 f7ab99df Sofia Papagiannaki
        """
187 3f5b02c7 Sofia Papagiannaki
        for key, val in meta.items():
188 f7ab99df Sofia Papagiannaki
            meta.pop(key)
189 f7ab99df Sofia Papagiannaki
            meta['X-%s-Meta-%s' %(entity.capitalize(), key.capitalize())] = val
190 f7ab99df Sofia Papagiannaki
        prev_meta = self._get_metadata(path)
191 f7ab99df Sofia Papagiannaki
        prev_meta.update(meta)
192 f7ab99df Sofia Papagiannaki
        headers = {}
193 f7ab99df Sofia Papagiannaki
        for key, val in prev_meta.items():
194 f7ab99df Sofia Papagiannaki
            headers[key.capitalize()] = val
195 f7ab99df Sofia Papagiannaki
        self.post(path, headers=headers)
196 f7ab99df Sofia Papagiannaki
    
197 f7ab99df Sofia Papagiannaki
    def _delete_metadata(self, path, entity, meta=[]):
198 f7ab99df Sofia Papagiannaki
        """
199 f7ab99df Sofia Papagiannaki
        delete previously set metadata
200 f7ab99df Sofia Papagiannaki
        """
201 f7ab99df Sofia Papagiannaki
        prev_meta = self._get_metadata(path)
202 f7ab99df Sofia Papagiannaki
        headers = {}
203 f7ab99df Sofia Papagiannaki
        for key, val in prev_meta.items():
204 f7ab99df Sofia Papagiannaki
            if key.split('-')[-1] in meta:
205 f7ab99df Sofia Papagiannaki
                continue
206 f7ab99df Sofia Papagiannaki
            http_key = key.capitalize()
207 3f5b02c7 Sofia Papagiannaki
            headers[http_key] = val
208 3f5b02c7 Sofia Papagiannaki
        self.post(path, headers=headers)
209 f7ab99df Sofia Papagiannaki
    
210 3f5b02c7 Sofia Papagiannaki
    # Storage Account Services
211 f7ab99df Sofia Papagiannaki
    
212 ec1b8d3e Sofia Papagiannaki
    def list_containers(self, detail=False, params=None, headers=None):
213 3f5b02c7 Sofia Papagiannaki
        return self._list('', detail, params, headers)
214 f7ab99df Sofia Papagiannaki
    
215 d2d5c360 Sofia Papagiannaki
    def account_metadata(self, restricted=False, until=None):
216 f7ab99df Sofia Papagiannaki
        prefix = 'x-account-meta-' if restricted else None
217 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
218 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('', prefix, params=params)
219 f7ab99df Sofia Papagiannaki
    
220 3f5b02c7 Sofia Papagiannaki
    def update_account_metadata(self, **meta):
221 f7ab99df Sofia Papagiannaki
        self._update_metadata('', 'account', **meta)
222 f7ab99df Sofia Papagiannaki
        
223 f7ab99df Sofia Papagiannaki
    def delete_account_metadata(self, meta=[]):
224 f7ab99df Sofia Papagiannaki
        self._delete_metadata('', 'account', meta)
225 f7ab99df Sofia Papagiannaki
    
226 3f5b02c7 Sofia Papagiannaki
    # Storage Container Services
227 f7ab99df Sofia Papagiannaki
    
228 ec1b8d3e Sofia Papagiannaki
    def list_objects(self, container, detail=False, params=None, headers=None):
229 ec1b8d3e Sofia Papagiannaki
        return self._list('/' + container, detail, params, headers)
230 f7ab99df Sofia Papagiannaki
    
231 ec1b8d3e Sofia Papagiannaki
    def create_container(self, container, headers=None):
232 ec1b8d3e Sofia Papagiannaki
        status, header, data = self.put('/' + container, headers=headers)
233 3f5b02c7 Sofia Papagiannaki
        if status == 202:
234 3f5b02c7 Sofia Papagiannaki
            return False
235 3f5b02c7 Sofia Papagiannaki
        elif status != 201:
236 e3fd7f91 Sofia Papagiannaki
            raise Fault(data, int(status))
237 3f5b02c7 Sofia Papagiannaki
        return True
238 f7ab99df Sofia Papagiannaki
    
239 3f5b02c7 Sofia Papagiannaki
    def delete_container(self, container):
240 3f5b02c7 Sofia Papagiannaki
        self.delete('/' + container)
241 f7ab99df Sofia Papagiannaki
    
242 d2d5c360 Sofia Papagiannaki
    def retrieve_container_metadata(self, container, restricted=False,
243 d2d5c360 Sofia Papagiannaki
                                    until=None):
244 f7ab99df Sofia Papagiannaki
        prefix = 'x-container-meta-' if restricted else None
245 f7ab99df Sofia Papagiannaki
        params = {'until':until} if until else None
246 d2d5c360 Sofia Papagiannaki
        return self._get_metadata('/%s' % container, prefix, params=params)
247 f7ab99df Sofia Papagiannaki
    
248 3f5b02c7 Sofia Papagiannaki
    def update_container_metadata(self, container, **meta):
249 f7ab99df Sofia Papagiannaki
        self._update_metadata('/' + container, 'container', **meta)
250 f7ab99df Sofia Papagiannaki
        
251 f7ab99df Sofia Papagiannaki
    def delete_container_metadata(self, container, meta=[]):
252 f7ab99df Sofia Papagiannaki
        path = '/%s' % (container)
253 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'container', meta)
254 f7ab99df Sofia Papagiannaki
    
255 3f5b02c7 Sofia Papagiannaki
    # Storage Object Services
256 f7ab99df Sofia Papagiannaki
    
257 8fe01d72 Sofia Papagiannaki
    def retrieve_object(self, container, object, detail=False, headers=None,
258 8fe01d72 Sofia Papagiannaki
                        version=None):
259 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
260 ec1b8d3e Sofia Papagiannaki
        format = 'json' if detail else 'text'
261 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None 
262 8fe01d72 Sofia Papagiannaki
        status, headers, data = self.get(path, format, headers, params)
263 3f5b02c7 Sofia Papagiannaki
        return data
264 f7ab99df Sofia Papagiannaki
    
265 f7ab99df Sofia Papagiannaki
    def create_directory_marker(self, container, object):
266 f7ab99df Sofia Papagiannaki
        if not object:
267 f7ab99df Sofia Papagiannaki
            raise Fault('Directory markers have to be nested in a container')
268 f7ab99df Sofia Papagiannaki
        h = {'Content-Type':'application/directory'}
269 f7ab99df Sofia Papagiannaki
        self.create_object(container, object, f=None, headers=h)
270 f7ab99df Sofia Papagiannaki
    
271 ec1b8d3e Sofia Papagiannaki
    def create_object(self, container, object, f=stdin, chunked=False,
272 ec1b8d3e Sofia Papagiannaki
                      blocksize=1024, headers=None):
273 d2d5c360 Sofia Papagiannaki
        """
274 d2d5c360 Sofia Papagiannaki
        creates an object
275 d2d5c360 Sofia Papagiannaki
        if f is None then creates a zero length object
276 d2d5c360 Sofia Papagiannaki
        if f is stdin or chunked is set then performs chunked transfer 
277 d2d5c360 Sofia Papagiannaki
        """
278 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
279 ec1b8d3e Sofia Papagiannaki
        if not chunked and f != stdin:
280 f7ab99df Sofia Papagiannaki
            data = f.read() if f else None
281 e3fd7f91 Sofia Papagiannaki
            return self.put(path, data, headers=headers)
282 ec1b8d3e Sofia Papagiannaki
        else:
283 e3fd7f91 Sofia Papagiannaki
            return self._chunked_transfer(path, 'PUT', f, headers=headers,
284 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
285 f7ab99df Sofia Papagiannaki
    
286 ec1b8d3e Sofia Papagiannaki
    def update_object(self, container, object, f=stdin, chunked=False,
287 ec1b8d3e Sofia Papagiannaki
                      blocksize=1024, headers=None):
288 ec1b8d3e Sofia Papagiannaki
        if not f:
289 ec1b8d3e Sofia Papagiannaki
            return
290 ec1b8d3e Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
291 ec1b8d3e Sofia Papagiannaki
        if not chunked and f != stdin:
292 ec1b8d3e Sofia Papagiannaki
            data = f.read()
293 ec1b8d3e Sofia Papagiannaki
            self.post(path, data, headers=headers)
294 ec1b8d3e Sofia Papagiannaki
        else:
295 ec1b8d3e Sofia Papagiannaki
            self._chunked_transfer(path, 'POST', f, headers=headers,
296 ec1b8d3e Sofia Papagiannaki
                                   blocksize=1024)
297 f7ab99df Sofia Papagiannaki
    
298 ec1b8d3e Sofia Papagiannaki
    def _change_obj_location(self, src_container, src_object, dst_container,
299 ad71a0ce Sofia Papagiannaki
                             dst_object, remove=False, headers=None):
300 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (dst_container, dst_object)
301 ad71a0ce Sofia Papagiannaki
        if not headers:
302 ad71a0ce Sofia Papagiannaki
            headers = {}
303 ec1b8d3e Sofia Papagiannaki
        if remove:
304 ec1b8d3e Sofia Papagiannaki
            headers['X-Move-From'] = '/%s/%s' % (src_container, src_object)
305 ec1b8d3e Sofia Papagiannaki
        else:
306 ec1b8d3e Sofia Papagiannaki
            headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
307 3f5b02c7 Sofia Papagiannaki
        headers['Content-Length'] = 0
308 3f5b02c7 Sofia Papagiannaki
        self.put(path, headers=headers)
309 f7ab99df Sofia Papagiannaki
    
310 ec1b8d3e Sofia Papagiannaki
    def copy_object(self, src_container, src_object, dst_container,
311 ad71a0ce Sofia Papagiannaki
                             dst_object, headers=None):
312 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
313 7e999dec Sofia Papagiannaki
                                   dst_container, dst_object,
314 7e999dec Sofia Papagiannaki
                                   headers=headers)
315 f7ab99df Sofia Papagiannaki
    
316 ec1b8d3e Sofia Papagiannaki
    def move_object(self, src_container, src_object, dst_container,
317 ad71a0ce Sofia Papagiannaki
                             dst_object, headers=None):
318 ec1b8d3e Sofia Papagiannaki
        self._change_obj_location(src_container, src_object,
319 ad71a0ce Sofia Papagiannaki
                                   dst_container, dst_object, True, headers)
320 f7ab99df Sofia Papagiannaki
    
321 3f5b02c7 Sofia Papagiannaki
    def delete_object(self, container, object):
322 3f5b02c7 Sofia Papagiannaki
        self.delete('/%s/%s' % (container, object))
323 f7ab99df Sofia Papagiannaki
    
324 8fe01d72 Sofia Papagiannaki
    def retrieve_object_metadata(self, container, object, restricted=False,
325 8fe01d72 Sofia Papagiannaki
                                 version=None):
326 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
327 f7ab99df Sofia Papagiannaki
        prefix = 'x-object-meta-' if restricted else None
328 f7ab99df Sofia Papagiannaki
        params = {'version':version} if version else None
329 8fe01d72 Sofia Papagiannaki
        return self._get_metadata(path, prefix, params=params)
330 f7ab99df Sofia Papagiannaki
    
331 3f5b02c7 Sofia Papagiannaki
    def update_object_metadata(self, container, object, **meta):
332 3f5b02c7 Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
333 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
334 f7ab99df Sofia Papagiannaki
    
335 f7ab99df Sofia Papagiannaki
    def delete_object_metadata(self, container, object, meta=[]):
336 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
337 f7ab99df Sofia Papagiannaki
        self._delete_metadata(path, 'object', meta)
338 f7ab99df Sofia Papagiannaki
    
339 f7ab99df Sofia Papagiannaki
    def trash_object(self, container, object):
340 f7ab99df Sofia Papagiannaki
        """
341 f7ab99df Sofia Papagiannaki
        trashes an object
342 f7ab99df Sofia Papagiannaki
        actually resets all object metadata with trash = true 
343 f7ab99df Sofia Papagiannaki
        """
344 f7ab99df Sofia Papagiannaki
        path = '/%s/%s' % (container, object)
345 f7ab99df Sofia Papagiannaki
        meta = {'trash':'true'}
346 f7ab99df Sofia Papagiannaki
        self._update_metadata(path, 'object', **meta)
347 f7ab99df Sofia Papagiannaki
    
348 f7ab99df Sofia Papagiannaki
    def restore_object(self, container, object):
349 f7ab99df Sofia Papagiannaki
        """
350 f7ab99df Sofia Papagiannaki
        restores a trashed object
351 f7ab99df Sofia Papagiannaki
        actualy just resets all object metadata except trash
352 f7ab99df Sofia Papagiannaki
        """
353 f7ab99df Sofia Papagiannaki
        self.delete_object_metadata(container, object, ['trash'])