Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / storage.py @ 3dabe5d2

History | View | Annotate | Download (8.3 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 kamaki.clients import Client, ClientError
35
from kamaki.clients.utils import filter_in, filter_out, path4url
36

    
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
        self.account = account
44
        self.container = container
45

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

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

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

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

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

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

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

    
99
    def delete_container(self, container):
100
        self.assert_account()
101
        path = path4url(self.account, container)
102
        r = self.delete(path, success=(204, 404, 409))
103
        if r.status_code == 404:
104
            raise ClientError("Container does not exist", r.status_code)
105
        elif r.status_code == 409:
106
            raise ClientError("Container is not empty", r.status_code)
107

    
108
    def list_containers(self):
109
        self.assert_account()
110
        self.set_param('format', 'json')
111
        path = path4url(self.account)
112
        r = self.get(path, success=(200, 204))
113
        reply = r.json
114
        return reply
115

    
116
    def upload_object(self, object, f, size=None):
117
        # This is a naive implementation, it loads the whole file in memory
118
        #Look in pithos for a nice implementation
119
        self.assert_container()
120
        path = path4url(self.account, self.container, object)
121
        data = f.read(size) if size is not None else f.read()
122
        r = self.put(path, data=data, success=201)
123
        r.release()
124

    
125
    def create_directory(self, object):
126
        self.assert_container()
127
        path = path4url(self.account, self.container, object)
128
        self.set_header('Content-Type', 'application/directory')
129
        self.set_header('Content-length', '0')
130
        r = self.put(path, success=201)
131
        r.release()
132

    
133
    def get_object_info(self, object):
134
        self.assert_container()
135
        path = path4url(self.account, self.container, object)
136
        r = self.head(path, success=200)
137
        reply = r.headers
138
        return reply
139

    
140
    def get_object_meta(self, object):
141
        r = filter_in(self.get_object_info(object), 'X-Object-Meta-')
142
        reply = {}
143
        for (key, val) in r.items():
144
            metakey = key.split('-')[-1]
145
            reply[metakey] = val
146
        return reply
147

    
148
    def del_object_meta(self, metakey, object):
149
        self.assert_container()
150
        self.set_header('X-Object-Meta-' + metakey, '')
151
        path = path4url(self.account, self.container, object)
152
        r = self.post(path, success=202)
153
        r.release()
154

    
155
    def replace_object_meta(self, metapairs):
156
        self.assert_container()
157
        path = path4url(self.account, self.container)
158
        for key, val in metapairs:
159
            self.set_header('X-Object-Meta-' + key, val)
160
        r = self.post(path, success=202)
161
        r.release()
162

    
163
    def get_object(self, object):
164
        self.assert_container()
165
        path = path4url(self.account, self.container, object)
166
        r = self.get(path, success=200)
167
        size = int(r.headers['content-length'])
168
        cnt = r.content
169
        return cnt, size
170

    
171
    def copy_object(self, src_container, src_object, dst_container,
172
        dst_object=False):
173
        self.assert_account()
174
        dst_object = dst_object or src_object
175
        dst_path = path4url(self.account, dst_container, dst_object)
176
        self.set_header('X-Copy-From', path4url(src_container, src_object))
177
        self.set_header('Content-Length', 0)
178
        r = self.put(dst_path, success=201)
179
        r.release()
180

    
181
    def move_object(self, src_container, src_object, dst_container,
182
        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-Move-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 delete_object(self, object):
192
        self.assert_container()
193
        path = path4url(self.account, self.container, object)
194
        r = self.delete(path, success=(204, 404))
195
        if r.status_code == 404:
196
            raise ClientError("Object %s not found" % object, r.status_code)
197

    
198
    def list_objects(self):
199
        self.assert_container()
200
        path = path4url(self.account, self.container)
201
        self.set_param('format', 'json')
202
        r = self.get(path, success=(200, 204, 304, 404), )
203
        if r.status_code == 404:
204
            raise ClientError(\
205
                "Incorrect account (%s) for that container" % self.account,
206
                r.status_code)
207
        elif r.status_code == 304:
208
            return []
209
        reply = r.json
210
        return reply
211

    
212
    def list_objects_in_path(self, path_prefix):
213
        self.assert_container()
214
        path = path4url(self.account, self.container)
215
        self.set_param('format', 'json')
216
        self.set_param('path', 'path_prefix')
217
        r = self.get(path, success=(200, 204, 404))
218
        if r.status_code == 404:
219
            raise ClientError(\
220
                "Incorrect account (%s) for that container" % self.account,
221
                r.status_code)
222
        reply = r.json
223
        return reply