~ubuntu-branches/ubuntu/trusty/swift/trusty-updates

« back to all changes in this revision

Viewing changes to swift/proxy/controllers/container.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Soren Hansen, Chuck Short
  • Date: 2012-09-07 19:02:36 UTC
  • mfrom: (1.2.12)
  • Revision ID: package-import@ubuntu.com-20120907190236-fqrmbzm7v6zivs8d
Tags: 1.7.0-0ubuntu1
[ Soren Hansen ]
* Update debian/watch to account for symbolically named tarballs and
  use newer URL.
* Run unit tests at build time.
* Fix Launchpad URLs in debian/watch.

[ Chuck Short ]
* New upstream release
* debian/control: Add pubthon-moc as a build dep
* debian/rules: Dont fail if testsuite fails.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2010-2012 OpenStack, LLC.
 
2
#
 
3
# Licensed under the Apache License, Version 2.0 (the "License");
 
4
# you may not use this file except in compliance with the License.
 
5
# You may obtain a copy of the License at
 
6
#
 
7
#    http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
# Unless required by applicable law or agreed to in writing, software
 
10
# distributed under the License is distributed on an "AS IS" BASIS,
 
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
12
# implied.
 
13
# See the License for the specific language governing permissions and
 
14
# limitations under the License.
 
15
 
 
16
# NOTE: swift_conn
 
17
# You'll see swift_conn passed around a few places in this file. This is the
 
18
# source httplib connection of whatever it is attached to.
 
19
#   It is used when early termination of reading from the connection should
 
20
# happen, such as when a range request is satisfied but there's still more the
 
21
# source connection would like to send. To prevent having to read all the data
 
22
# that could be left, the source connection can be .close() and then reads
 
23
# commence to empty out any buffers.
 
24
#   These shenanigans are to ensure all related objects can be garbage
 
25
# collected. We've seen objects hang around forever otherwise.
 
26
 
 
27
import time
 
28
from urllib import unquote
 
29
from random import shuffle
 
30
 
 
31
from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPNotFound
 
32
 
 
33
from swift.common.utils import normalize_timestamp, public
 
34
from swift.common.constraints import check_metadata, MAX_CONTAINER_NAME_LENGTH
 
35
from swift.common.http import HTTP_ACCEPTED
 
36
from swift.proxy.controllers.base import Controller, delay_denial, \
 
37
    get_container_memcache_key
 
38
 
 
39
 
 
40
class ContainerController(Controller):
 
41
    """WSGI controller for container requests"""
 
42
    server_type = 'Container'
 
43
 
 
44
    # Ensure these are all lowercase
 
45
    pass_through_headers = ['x-container-read', 'x-container-write',
 
46
                            'x-container-sync-key', 'x-container-sync-to',
 
47
                            'x-versions-location']
 
48
 
 
49
    def __init__(self, app, account_name, container_name, **kwargs):
 
50
        Controller.__init__(self, app)
 
51
        self.account_name = unquote(account_name)
 
52
        self.container_name = unquote(container_name)
 
53
 
 
54
    def clean_acls(self, req):
 
55
        if 'swift.clean_acl' in req.environ:
 
56
            for header in ('x-container-read', 'x-container-write'):
 
57
                if header in req.headers:
 
58
                    try:
 
59
                        req.headers[header] = \
 
60
                            req.environ['swift.clean_acl'](header,
 
61
                                                           req.headers[header])
 
62
                    except ValueError, err:
 
63
                        return HTTPBadRequest(request=req, body=str(err))
 
64
        return None
 
65
 
 
66
    def GETorHEAD(self, req):
 
67
        """Handler for HTTP GET/HEAD requests."""
 
68
        if not self.account_info(self.account_name)[1]:
 
69
            return HTTPNotFound(request=req)
 
70
        part, nodes = self.app.container_ring.get_nodes(
 
71
                        self.account_name, self.container_name)
 
72
        shuffle(nodes)
 
73
        resp = self.GETorHEAD_base(req, _('Container'), part, nodes,
 
74
                req.path_info, len(nodes))
 
75
 
 
76
        if self.app.memcache:
 
77
            # set the memcache container size for ratelimiting
 
78
            cache_key = get_container_memcache_key(self.account_name,
 
79
                                                   self.container_name)
 
80
            self.app.memcache.set(cache_key,
 
81
              {'status': resp.status_int,
 
82
               'read_acl': resp.headers.get('x-container-read'),
 
83
               'write_acl': resp.headers.get('x-container-write'),
 
84
               'sync_key': resp.headers.get('x-container-sync-key'),
 
85
               'container_size': resp.headers.get('x-container-object-count'),
 
86
               'versions': resp.headers.get('x-versions-location')},
 
87
                                  timeout=self.app.recheck_container_existence)
 
88
 
 
89
        if 'swift.authorize' in req.environ:
 
90
            req.acl = resp.headers.get('x-container-read')
 
91
            aresp = req.environ['swift.authorize'](req)
 
92
            if aresp:
 
93
                return aresp
 
94
        if not req.environ.get('swift_owner', False):
 
95
            for key in ('x-container-read', 'x-container-write',
 
96
                        'x-container-sync-key', 'x-container-sync-to'):
 
97
                if key in resp.headers:
 
98
                    del resp.headers[key]
 
99
        return resp
 
100
 
 
101
    @public
 
102
    @delay_denial
 
103
    def GET(self, req):
 
104
        """Handler for HTTP GET requests."""
 
105
        return self.GETorHEAD(req)
 
106
 
 
107
    @public
 
108
    @delay_denial
 
109
    def HEAD(self, req):
 
110
        """Handler for HTTP HEAD requests."""
 
111
        return self.GETorHEAD(req)
 
112
 
 
113
    @public
 
114
    def PUT(self, req):
 
115
        """HTTP PUT request handler."""
 
116
        error_response = \
 
117
            self.clean_acls(req) or check_metadata(req, 'container')
 
118
        if error_response:
 
119
            return error_response
 
120
        if len(self.container_name) > MAX_CONTAINER_NAME_LENGTH:
 
121
            resp = HTTPBadRequest(request=req)
 
122
            resp.body = 'Container name length of %d longer than %d' % \
 
123
                        (len(self.container_name), MAX_CONTAINER_NAME_LENGTH)
 
124
            return resp
 
125
        account_partition, accounts, container_count = \
 
126
            self.account_info(self.account_name,
 
127
                              autocreate=self.app.account_autocreate)
 
128
        if self.app.max_containers_per_account > 0 and \
 
129
                container_count >= self.app.max_containers_per_account and \
 
130
                self.account_name not in self.app.max_containers_whitelist:
 
131
            resp = HTTPForbidden(request=req)
 
132
            resp.body = 'Reached container limit of %s' % \
 
133
                        self.app.max_containers_per_account
 
134
            return resp
 
135
        if not accounts:
 
136
            return HTTPNotFound(request=req)
 
137
        container_partition, containers = self.app.container_ring.get_nodes(
 
138
            self.account_name, self.container_name)
 
139
        headers = []
 
140
        for account in accounts:
 
141
            nheaders = {'X-Timestamp': normalize_timestamp(time.time()),
 
142
                        'x-trans-id': self.trans_id,
 
143
                        'X-Account-Host': '%(ip)s:%(port)s' % account,
 
144
                        'X-Account-Partition': account_partition,
 
145
                        'X-Account-Device': account['device'],
 
146
                        'Connection': 'close'}
 
147
            self.transfer_headers(req.headers, nheaders)
 
148
            headers.append(nheaders)
 
149
        if self.app.memcache:
 
150
            cache_key = get_container_memcache_key(self.account_name,
 
151
                                                   self.container_name)
 
152
            self.app.memcache.delete(cache_key)
 
153
        resp = self.make_requests(req, self.app.container_ring,
 
154
                container_partition, 'PUT', req.path_info, headers)
 
155
        return resp
 
156
 
 
157
    @public
 
158
    def POST(self, req):
 
159
        """HTTP POST request handler."""
 
160
        error_response = \
 
161
            self.clean_acls(req) or check_metadata(req, 'container')
 
162
        if error_response:
 
163
            return error_response
 
164
        account_partition, accounts, container_count = \
 
165
            self.account_info(self.account_name,
 
166
                              autocreate=self.app.account_autocreate)
 
167
        if not accounts:
 
168
            return HTTPNotFound(request=req)
 
169
        container_partition, containers = self.app.container_ring.get_nodes(
 
170
            self.account_name, self.container_name)
 
171
        headers = {'X-Timestamp': normalize_timestamp(time.time()),
 
172
                   'x-trans-id': self.trans_id,
 
173
                   'Connection': 'close'}
 
174
        self.transfer_headers(req.headers, headers)
 
175
        if self.app.memcache:
 
176
            cache_key = get_container_memcache_key(self.account_name,
 
177
                                                   self.container_name)
 
178
            self.app.memcache.delete(cache_key)
 
179
        resp = self.make_requests(req, self.app.container_ring,
 
180
                container_partition, 'POST', req.path_info,
 
181
                [headers] * len(containers))
 
182
        return resp
 
183
 
 
184
    @public
 
185
    def DELETE(self, req):
 
186
        """HTTP DELETE request handler."""
 
187
        account_partition, accounts, container_count = \
 
188
            self.account_info(self.account_name)
 
189
        if not accounts:
 
190
            return HTTPNotFound(request=req)
 
191
        container_partition, containers = self.app.container_ring.get_nodes(
 
192
            self.account_name, self.container_name)
 
193
        headers = []
 
194
        for account in accounts:
 
195
            headers.append({'X-Timestamp': normalize_timestamp(time.time()),
 
196
                           'X-Trans-Id': self.trans_id,
 
197
                           'X-Account-Host': '%(ip)s:%(port)s' % account,
 
198
                           'X-Account-Partition': account_partition,
 
199
                           'X-Account-Device': account['device'],
 
200
                           'Connection': 'close'})
 
201
        if self.app.memcache:
 
202
            cache_key = get_container_memcache_key(self.account_name,
 
203
                                                   self.container_name)
 
204
            self.app.memcache.delete(cache_key)
 
205
        resp = self.make_requests(req, self.app.container_ring,
 
206
                    container_partition, 'DELETE', req.path_info, headers)
 
207
        # Indicates no server had the container
 
208
        if resp.status_int == HTTP_ACCEPTED:
 
209
            return HTTPNotFound(request=req)
 
210
        return resp