Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / storage.py @ afd9d603

History | View | Annotate | Download (8.8 kB)

1
#a Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from . import Client, ClientError
35
from .utils import filter_in, filter_out, prefix_keys, path4url
36
#from .connection.kamakicon import KamakiHTTPConnection
37

    
38
class StorageClient(Client):
39
    """OpenStack Object Storage API 1.0 client"""
40

    
41
    def __init__(self, base_url, token, account=None, container=None):
42
        super(StorageClient, self).__init__(base_url, token)
43
        #super(StorageClient, self).__init__(base_url, token, http_client=KamakiHTTPConnection())
44
        self.account = account
45
        self.container = container
46

    
47
    def assert_account(self):
48
        if not self.account:
49
            raise ClientError("No account provided")
50

    
51
    def assert_container(self):
52
        self.assert_account()
53
        if not self.container:
54
            raise ClientError("No container provided")
55

    
56
    def get_account_info(self):
57
        self.assert_account()
58
        path = path4url(self.account)
59
        r = self.head(path, success=(204, 401))
60
        if r.status_code == 401:
61
            r.release()
62
            raise ClientError("No authorization")
63
        reply = r.headers
64
        r.release()
65
        return reply
66

    
67
    def replace_account_meta(self, metapairs):
68
        self.assert_account()
69
        path = path4url(self.account)
70
        for key, val in  metapairs:
71
            self.set_header('X-Account-Meta-'+key, val)
72
        r = self.post(path, success=202)
73
        r.release()
74

    
75
    def del_account_meta(self, metakey):
76
        headers = self.get_account_info()
77
        self.headers = filter_out(headers, 'X-Account-Meta-'+metakey, exactMatch = True)
78
        if len(self.headers) == len(headers):
79
            raise ClientError('X-Account-Meta-%s not found' % metakey, 404)
80
        path = path4url(self.account)
81
        r = self.post(path, success = 202)
82
        r.release()
83

    
84
    def create_container(self, container):
85
        self.assert_account()
86
        path = path4url(self.account, container)
87
        r = self.put(path, success=(201, 202))
88
        if r.status_code == 202:
89
            r.release()
90
            raise ClientError("Container already exists", r.status_code)
91
        r.release()
92

    
93
    def get_container_info(self, container):
94
        self.assert_account()
95
        path = path4url(self.account, container)
96
        r = self.head(path, success=(204, 404))
97
        if r.status_code == 404:
98
            r.release()
99
            raise ClientError("Container does not exist", r.status_code)
100
        reply = r.headers
101
        r.release()
102
        return reply
103

    
104
    def delete_container(self, container):
105
        self.assert_account()
106
        path = path4url(self.account, container)
107
        r = self.delete(path, success=(204, 404, 409))
108
        if r.status_code == 404:
109
            r.release()
110
            raise ClientError("Container does not exist", r.status_code)
111
        elif r.status_code == 409:
112
            r.release()
113
            raise ClientError("Container is not empty", r.status_code)
114
        r.release()
115

    
116
    def list_containers(self):
117
        self.assert_account()
118
        self.set_param('format', 'json')
119
        path = path4url(self.account)
120
        r = self.get(path, success = (200, 204))
121
        reply = r.json
122
        r.release()
123
        return reply
124

    
125
    def upload_object(self, object, f, size=None):
126
        # This is a naive implementation, it loads the whole file in memory
127
        #Look in pithos for a nice implementation
128
        self.assert_container()
129
        path = path4url(self.account, self.container, object)
130
        data = f.read(size) if size is not None else f.read()
131
        r = self.put(path, data=data, success=201)
132
        r.release()
133

    
134
    def create_directory(self, object):
135
        self.assert_container()
136
        path = path4url(self.account, self.container, object)
137
        self.set_header('Content-Type', 'application/directory')
138
        self.set_header('Content-length', '0')
139
        r = self.put(path, success=201)
140
        r.release()
141

    
142
    def get_object_info(self, object):
143
        self.assert_container()
144
        path = path4url(self.account, self.container, object)
145
        r = self.head(path, success=200)
146
        reply = r.headers
147
        r.release()
148
        return reply
149

    
150
    def get_object_meta(self, object):
151
        r = filter_in(self.get_object_info(object), 'X-Object-Meta-')
152
        reply = {}
153
        for (key, val) in r.items():
154
            metakey = key.split('-')[-1]
155
            reply[metakey] = val
156
        return reply
157

    
158
    def del_object_meta(self, metakey, object):
159
        self.assert_container()
160
        self.set_header('X-Object-Meta-'+metakey, '')
161
        path = path4url(self.account, self.container, object)
162
        r = self.post(path, success = 202)
163
        r.release()
164

    
165
    def replace_object_meta(self, metapairs):
166
        self.assert_container()
167
        path=path4url(self.account, self.container)
168
        for key, val in metapairs:
169
            self.set_header('X-Object-Meta-'+key, val)
170
        r = self.post(path, success=202)
171
        r.release()
172

    
173
    def get_object(self, object):
174
        self.assert_container()
175
        path = path4url(self.account, self.container, object)
176
        r = self.get(path, success=200)
177
        size = int(r.headers['content-length'])
178
        cnt = r.content
179
        r.release()
180
        return cnt, size
181

    
182
    def copy_object(self, src_container, src_object, dst_container, dst_object=False):
183
        self.assert_account()
184
        dst_object = dst_object or src_object
185
        dst_path = path4url(self.account, dst_container, dst_object)
186
        self.set_header('X-Copy-From', path4url(src_container, src_object))
187
        self.set_header('Content-Length', 0)
188
        r = self.put(dst_path, success=201)
189
        r.release()
190

    
191
    def move_object(self, src_container, src_object, dst_container, dst_object=False):
192
        self.assert_account()
193
        dst_object = dst_object or src_object
194
        dst_path = path4url(self.account, dst_container, dst_object)
195
        self.set_header('X-Move-From', path4url(src_container, src_object))
196
        self.set_header('Content-Length', 0)
197
        r = self.put(dst_path, success=201)
198
        r.release()
199

    
200
    def delete_object(self, object):
201
        self.assert_container()
202
        path = path4url(self.account, self.container, object)
203
        r = self.delete(path, success=(204, 404))
204
        if r.status_code == 404:
205
            r.release()
206
            raise ClientError("Object %s not found" %object, r.status_code)
207
        r.release()
208
       
209
    def list_objects(self):
210
        self.assert_container()
211
        path = path4url(self.account, self.container)
212
        self.set_param('format', 'json')
213
        r = self.get(path, success=(200, 204, 304, 404), )
214
        if r.status_code == 404:
215
            r.release()
216
            raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
217
        elif r.status_code == 304:
218
            r.release()
219
            return []
220
        reply = r.json
221
        r.release()
222
        return reply
223

    
224
    def list_objects_in_path(self, path_prefix):
225
        self.assert_container()
226
        path = path4url(self.account, self.container)
227
        self.set_param('format', 'json')
228
        self.set_param('path', 'path_prefix')
229
        r = self.get(path, success=(200, 204, 404))
230
        if r.status_code == 404:
231
            r.release()
232
            raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
233
        reply = r.json
234
        r.release()
235
        return reply
236