Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / storage.py @ 9f74ca46

History | View | Annotate | Download (8 kB)

1
# 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, params4url
36

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

    
40
    def __init__(self, base_url, token, account=None, container=None):
41
        super(StorageClient, self).__init__(base_url, token)
42
        self.account = account
43
        self.container = container
44

    
45
    def assert_account(self):
46
        if not self.account:
47
            raise ClientError("Please provide an account")
48

    
49
    def assert_container(self):
50
        self.assert_account()
51
        if not self.container:
52
            raise ClientError("Please provide a container")
53

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

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

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

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

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

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

    
101
    def list_containers(self):
102
        self.assert_account()
103
        path = path4url(self.account)+params4url(dict(format='json'))
104
        r = self.get(path, success = (200, 204))
105
        return r.json
106

    
107
    def create_object(self, object, f, size=None, hash_cb=None,
108
                      upload_cb=None):
109
        # This is a naive implementation, it loads the whole file in memory
110
        #Look in pithos for a nice implementation
111
        self.assert_container()
112
        path = path4url(self.account, self.container, object)
113
        data = f.read(size) if size is not None else f.read()
114
        self.put(path, data=data, success=201)
115

    
116
    def create_directory(self, object):
117
        self.assert_container()
118
        path = path4url(self.account, self.container, object)
119
        self.headers.setdefault('Content-Type', 'application/directory')
120
        self.headers.setdefault('Content-length', '0')
121
        self.put(path, success=201)
122

    
123
    def get_object_info(self, object):
124
        self.assert_container()
125
        path = path4url(self.account, self.container, object)
126
        r = self.head(path, success=200)
127
        return r.headers
128

    
129
    def get_object_meta(self, object):
130
        return filter_in(self.get_object_info(object), 'X-Object-Meta-')
131

    
132
    def delete_object_meta(self, metakey, object):
133
        self.assert_container()
134
        headers = filter_in(self.get_object_info(object), 'X-')
135
        self.headers = filter_out(headers, 'X-Object-Meta-'+metakey, exactMatch = True)
136
        if len(self.headers) == len(headers):
137
            raise ClientError('X-Object-Meta-%s not found' % metakey, 404)
138
        path = path4url(self.account, self.container, object)
139
        self.post(path, success = 202)
140

    
141
    def replace_object_meta(self, metapairs):
142
        self.assert_container()
143
        path=path4url(self.account, self.container)
144
        for key, val in metapairs:
145
            self.set_header('X-Object-Meta-'+key, val)
146
        self.post(path, success=202)
147

    
148
    def get_object(self, object):
149
        self.assert_container()
150
        path = path4url(self.account, self.container, object)
151
        r = self.get(path, raw=True, success=200)
152
        size = int(r.headers['content-length'])
153
        return r.raw, size
154

    
155
    def copy_object(self, src_container, src_object, dst_container, dst_object=False):
156
        self.assert_account()
157
        dst_object = dst_object or src_object
158
        dst_path = path4url(self.account, dst_container, dst_object)
159
        self.set_header('X-Copy-From', path4url(src_container, src_object))
160
        self.set_header('Content-Length', 0)
161
        self.put(dst_path, success=201)
162

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

    
171
    def delete_object(self, object):
172
        self.assert_container()
173
        path = path4url(self.account, self.container, object)
174
        r = self.delete(path, success=(204, 404))
175
        if r.status_code == 404:
176
            raise ClientError("Object %s not found" %object, r.status_code)
177
       
178
    def list_objects(self):
179
        self.assert_container()
180
        path = path4url(self.account, self.container)
181
        params = dict(format='json') #request parameters
182
        r = self.get(path, params=params, success=(200, 204, 404))
183
        if r.status_code == 404:
184
            raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
185
        return r.json
186

    
187
    def list_objects_in_path(self, path_prefix):
188
        self.assert_container()
189
        path = path4url(self.account, self.container)
190
        params = dict(format='json', path=path_prefix) #request parameters
191
        r = self.get(path, params=params, success=(200, 204, 404))
192
        if r.status_code == 404:
193
            raise ClientError("Incorrect account (%s) for that container"%self.account, r.status_code)
194
        return r.json
195