Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / storage.py @ c270fe96

History | View | Annotate | Download (8.2 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, 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
            raise ClientError("No authorization")
62
        reply = r.headers
63
        return reply
64

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

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

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

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

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

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

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

    
121
    def create_directory(self, object):
122
        self.assert_container()
123
        path = path4url(self.account, self.container, object)
124
        self.set_header('Content-Type', 'application/directory')
125
        self.set_header('Content-length', '0')
126
        r = self.put(path, success=201)
127

    
128
    def get_object_info(self, object):
129
        self.assert_container()
130
        path = path4url(self.account, self.container, object)
131
        r = self.head(path, success=200)
132
        reply = r.headers
133
        return reply
134

    
135
    def get_object_meta(self, object):
136
        r = filter_in(self.get_object_info(object), 'X-Object-Meta-')
137
        reply = {}
138
        for (key, val) in r.items():
139
            metakey = key.split('-')[-1]
140
            reply[metakey] = val
141
        return reply
142

    
143
    def del_object_meta(self, metakey, object):
144
        self.assert_container()
145
        self.set_header('X-Object-Meta-'+metakey, '')
146
        path = path4url(self.account, self.container, object)
147
        r = self.post(path, success = 202)
148

    
149
    def replace_object_meta(self, metapairs):
150
        self.assert_container()
151
        path=path4url(self.account, self.container)
152
        for key, val in metapairs:
153
            self.set_header('X-Object-Meta-'+key, val)
154
        r = self.post(path, success=202)
155

    
156
    def get_object(self, object):
157
        self.assert_container()
158
        path = path4url(self.account, self.container, object)
159
        r = self.get(path, success=200)
160
        size = int(r.headers['content-length'])
161
        cnt = r.content
162
        return cnt, size
163

    
164
    def copy_object(self, src_container, src_object, dst_container, dst_object=False):
165
        self.assert_account()
166
        dst_object = dst_object or src_object
167
        dst_path = path4url(self.account, dst_container, dst_object)
168
        self.set_header('X-Copy-From', path4url(src_container, src_object))
169
        self.set_header('Content-Length', 0)
170
        r = self.put(dst_path, success=201)
171

    
172
    def move_object(self, src_container, src_object, dst_container, 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-Move-From', path4url(src_container, src_object))
177
        self.set_header('Content-Length', 0)
178
        r = self.put(dst_path, success=201)
179

    
180
    def delete_object(self, object):
181
        self.assert_container()
182
        path = path4url(self.account, self.container, object)
183
        r = self.delete(path, success=(204, 404))
184
        if r.status_code == 404:
185
            raise ClientError("Object %s not found" %object, r.status_code)
186
       
187
    def list_objects(self):
188
        self.assert_container()
189
        path = path4url(self.account, self.container)
190
        self.set_param('format', 'json')
191
        r = self.get(path, success=(200, 204, 304, 404), )
192
        if r.status_code == 404:
193
            raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
194
        elif r.status_code == 304:
195
            return []
196
        reply = r.json
197
        return reply
198

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