Merging the upstream
This commit is contained in:
commit
ec58618eb3
2
bin/st
2
bin/st
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/python -u
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
67
bin/swauth-add-account
Executable file
67
bin/swauth-add-account
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='Usage: %prog [options] <account>')
|
||||||
|
parser.add_option('-s', '--suffix', dest='suffix',
|
||||||
|
default='', help='The suffix to use with the reseller prefix as the '
|
||||||
|
'storage account name (default: <randomly-generated-uuid4>) Note: If '
|
||||||
|
'the account already exists, this will have no effect on existing '
|
||||||
|
'service URLs. Those will need to be updated with '
|
||||||
|
'swauth-set-account-service')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/)')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
account = args[0]
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/%s' % (parsed.path, account)
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
if options.suffix:
|
||||||
|
headers['X-Account-Suffix'] = options.suffix
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'Account creation failed: %s %s' % (resp.status, resp.reason)
|
92
bin/swauth-add-user
Executable file
92
bin/swauth-add-user
Executable file
@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(
|
||||||
|
usage='Usage: %prog [options] <account> <user> <password>')
|
||||||
|
parser.add_option('-a', '--admin', dest='admin', action='store_true',
|
||||||
|
default=False, help='Give the user administrator access; otherwise '
|
||||||
|
'the user will only have access to containers specifically allowed '
|
||||||
|
'with ACLs.')
|
||||||
|
parser.add_option('-r', '--reseller-admin', dest='reseller_admin',
|
||||||
|
action='store_true', default=False, help='Give the user full reseller '
|
||||||
|
'administrator access, giving them full access to all accounts within '
|
||||||
|
'the reseller, including the ability to create new accounts. Creating '
|
||||||
|
'a new reseller admin requires super_admin rights.')
|
||||||
|
parser.add_option('-s', '--suffix', dest='suffix',
|
||||||
|
default='', help='The suffix to use with the reseller prefix as the '
|
||||||
|
'storage account name (default: <randomly-generated-uuid4>) Note: If '
|
||||||
|
'the account already exists, this will have no effect on existing '
|
||||||
|
'service URLs. Those will need to be updated with '
|
||||||
|
'swauth-set-account-service')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 3:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
account, user, password = args
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
# Ensure the account exists
|
||||||
|
path = '%sv2/%s' % (parsed.path, account)
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
if options.suffix:
|
||||||
|
headers['X-Account-Suffix'] = options.suffix
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'Account creation failed: %s %s' % (resp.status, resp.reason)
|
||||||
|
# Add the user
|
||||||
|
path = '%sv2/%s/%s' % (parsed.path, account, user)
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key,
|
||||||
|
'X-Auth-User-Key': password}
|
||||||
|
if options.admin:
|
||||||
|
headers['X-Auth-User-Admin'] = 'true'
|
||||||
|
if options.reseller_admin:
|
||||||
|
headers['X-Auth-User-Reseller-Admin'] = 'true'
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'PUT', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'User creation failed: %s %s' % (resp.status, resp.reason)
|
104
bin/swauth-cleanup-tokens
Executable file
104
bin/swauth-cleanup-tokens
Executable file
@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as json
|
||||||
|
except ImportError:
|
||||||
|
import json
|
||||||
|
import gettext
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from optparse import OptionParser
|
||||||
|
from sys import argv, exit
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
from swift.common.client import Connection
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='Usage: %prog [options]')
|
||||||
|
parser.add_option('-t', '--token-life', dest='token_life',
|
||||||
|
default='86400', help='The expected life of tokens; token objects '
|
||||||
|
'modified more than this number of seconds ago will be checked for '
|
||||||
|
'expiration (default: 86400).')
|
||||||
|
parser.add_option('-s', '--sleep', dest='sleep',
|
||||||
|
default='0.1', help='The number of seconds to sleep between token '
|
||||||
|
'checks (default: 0.1)')
|
||||||
|
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
||||||
|
default=False, help='Outputs everything done instead of just the '
|
||||||
|
'deletions.')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/)')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for .super_admin.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 0:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
options.admin_url = options.admin_url.rstrip('/')
|
||||||
|
if not options.admin_url.endswith('/v1.0'):
|
||||||
|
options.admin_url += '/v1.0'
|
||||||
|
options.admin_user = '.super_admin:.super_admin'
|
||||||
|
options.token_life = timedelta(0, float(options.token_life))
|
||||||
|
options.sleep = float(options.sleep)
|
||||||
|
conn = Connection(options.admin_url, options.admin_user, options.admin_key)
|
||||||
|
for x in xrange(16):
|
||||||
|
container = '.token_%x' % x
|
||||||
|
marker = None
|
||||||
|
while True:
|
||||||
|
if options.verbose:
|
||||||
|
print 'GET %s?marker=%s' % (container, marker)
|
||||||
|
objs = conn.get_container(container, marker=marker)[1]
|
||||||
|
if objs:
|
||||||
|
marker = objs[-1]['name']
|
||||||
|
else:
|
||||||
|
if options.verbose:
|
||||||
|
print 'No more objects in %s' % container
|
||||||
|
break
|
||||||
|
for obj in objs:
|
||||||
|
last_modified = datetime(*map(int, re.split('[^\d]',
|
||||||
|
obj['last_modified'])[:-1]))
|
||||||
|
ago = datetime.utcnow() - last_modified
|
||||||
|
if ago > options.token_life:
|
||||||
|
if options.verbose:
|
||||||
|
print '%s/%s last modified %ss ago; investigating' % \
|
||||||
|
(container, obj['name'],
|
||||||
|
ago.days * 86400 + ago.seconds)
|
||||||
|
print 'GET %s/%s' % (container, obj['name'])
|
||||||
|
detail = conn.get_object(container, obj['name'])[1]
|
||||||
|
detail = json.loads(detail)
|
||||||
|
if detail['expires'] < time():
|
||||||
|
if options.verbose:
|
||||||
|
print '%s/%s expired %ds ago; deleting' % \
|
||||||
|
(container, obj['name'],
|
||||||
|
time() - detail['expires'])
|
||||||
|
print 'DELETE %s/%s' % (container, obj['name'])
|
||||||
|
conn.delete_object(container, obj['name'])
|
||||||
|
elif options.verbose:
|
||||||
|
print "%s/%s won't expire for %ds; skipping" % \
|
||||||
|
(container, obj['name'],
|
||||||
|
detail['expires'] - time())
|
||||||
|
elif options.verbose:
|
||||||
|
print '%s/%s last modified %ss ago; skipping' % \
|
||||||
|
(container, obj['name'],
|
||||||
|
ago.days * 86400 + ago.seconds)
|
||||||
|
sleep(options.sleep)
|
||||||
|
if options.verbose:
|
||||||
|
print 'Done.'
|
59
bin/swauth-delete-account
Executable file
59
bin/swauth-delete-account
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='Usage: %prog [options] <account>')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
account = args[0]
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/%s' % (parsed.path, account)
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'DELETE', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'Account deletion failed: %s %s' % (resp.status, resp.reason)
|
59
bin/swauth-delete-user
Executable file
59
bin/swauth-delete-user
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='Usage: %prog [options] <account> <user>')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 2:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
account, user = args
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/%s/%s' % (parsed.path, account, user)
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'DELETE', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'User deletion failed: %s %s' % (resp.status, resp.reason)
|
85
bin/swauth-list
Executable file
85
bin/swauth-list
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as json
|
||||||
|
except ImportError:
|
||||||
|
import json
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='''
|
||||||
|
Usage: %prog [options] [account] [user]
|
||||||
|
|
||||||
|
If [account] and [user] are omitted, a list of accounts will be output.
|
||||||
|
|
||||||
|
If [account] is included but not [user], an account's information will be
|
||||||
|
output, including a list of users within the account.
|
||||||
|
|
||||||
|
If [account] and [user] are included, the user's information will be output,
|
||||||
|
including a list of groups the user belongs to.
|
||||||
|
|
||||||
|
If the [user] is '.groups', the active groups for the account will be listed.
|
||||||
|
'''.strip())
|
||||||
|
parser.add_option('-p', '--plain-text', dest='plain_text',
|
||||||
|
action='store_true', default=False, help='Changes the output from '
|
||||||
|
'JSON to plain text. This will cause an account to list only the '
|
||||||
|
'users and a user to list only the groups.')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) > 2:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/%s' % (parsed.path, '/'.join(args))
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'GET', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'List failed: %s %s' % (resp.status, resp.reason)
|
||||||
|
body = resp.read()
|
||||||
|
if options.plain_text:
|
||||||
|
info = json.loads(body)
|
||||||
|
for group in info[['accounts', 'users', 'groups'][len(args)]]:
|
||||||
|
print group['name']
|
||||||
|
else:
|
||||||
|
print body
|
58
bin/swauth-prep
Executable file
58
bin/swauth-prep
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='Usage: %prog [options]')
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if args:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/.prep' % parsed.path
|
||||||
|
headers = {'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'POST', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'Auth subsystem prep failed: %s %s' % (resp.status, resp.reason)
|
72
bin/swauth-set-account-service
Executable file
72
bin/swauth-set-account-service
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
try:
|
||||||
|
import simplejson as json
|
||||||
|
except ImportError:
|
||||||
|
import json
|
||||||
|
import gettext
|
||||||
|
from optparse import OptionParser
|
||||||
|
from os.path import basename
|
||||||
|
from sys import argv, exit
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
parser = OptionParser(usage='''
|
||||||
|
Usage: %prog [options] <account> <service> <name> <value>
|
||||||
|
|
||||||
|
Sets a service URL for an account. Can only be set by a reseller admin.
|
||||||
|
|
||||||
|
Example: %prog -K swauthkey test storage local http://127.0.0.1:8080/v1/AUTH_018c3946-23f8-4efb-a8fb-b67aae8e4162
|
||||||
|
'''.strip())
|
||||||
|
parser.add_option('-A', '--admin-url', dest='admin_url',
|
||||||
|
default='http://127.0.0.1:8080/auth/', help='The URL to the auth '
|
||||||
|
'subsystem (default: http://127.0.0.1:8080/auth/)')
|
||||||
|
parser.add_option('-U', '--admin-user', dest='admin_user',
|
||||||
|
default='.super_admin', help='The user with admin rights to add users '
|
||||||
|
'(default: .super_admin).')
|
||||||
|
parser.add_option('-K', '--admin-key', dest='admin_key',
|
||||||
|
help='The key for the user with admin rights to add users.')
|
||||||
|
args = argv[1:]
|
||||||
|
if not args:
|
||||||
|
args.append('-h')
|
||||||
|
(options, args) = parser.parse_args(args)
|
||||||
|
if len(args) != 4:
|
||||||
|
parser.parse_args(['-h'])
|
||||||
|
account, service, name, url = args
|
||||||
|
parsed = urlparse(options.admin_url)
|
||||||
|
if parsed.scheme not in ('http', 'https'):
|
||||||
|
raise Exception('Cannot handle protocol scheme %s for url %s' %
|
||||||
|
(parsed.scheme, repr(options.admin_url)))
|
||||||
|
if not parsed.path:
|
||||||
|
parsed.path = '/'
|
||||||
|
elif parsed.path[-1] != '/':
|
||||||
|
parsed.path += '/'
|
||||||
|
path = '%sv2/%s/.services' % (parsed.path, account)
|
||||||
|
body = json.dumps({service: {name: url}})
|
||||||
|
headers = {'Content-Length': str(len(body)),
|
||||||
|
'X-Auth-Admin-User': options.admin_user,
|
||||||
|
'X-Auth-Admin-Key': options.admin_key}
|
||||||
|
conn = http_connect(parsed.hostname, parsed.port, 'POST', path, headers,
|
||||||
|
ssl=(parsed.scheme == 'https'))
|
||||||
|
conn.send(body)
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status // 100 != 2:
|
||||||
|
print 'Service set failed: %s %s' % (resp.status, resp.reason)
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
46
bin/swift-auth-to-swauth
Executable file
46
bin/swift-auth-to-swauth
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2010 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import gettext
|
||||||
|
from subprocess import call
|
||||||
|
from sys import argv, exit
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gettext.install('swift', unicode=1)
|
||||||
|
if len(argv) != 4 or argv[1] != '-K':
|
||||||
|
exit('Syntax: %s -K <super_admin_key> <path to auth.db>' % argv[0])
|
||||||
|
_, _, super_admin_key, auth_db = argv
|
||||||
|
call(['swauth-prep', '-K', super_admin_key])
|
||||||
|
conn = sqlite3.connect(auth_db)
|
||||||
|
for account, cfaccount, user, password, admin, reseller_admin in \
|
||||||
|
conn.execute('SELECT account, cfaccount, user, password, admin, '
|
||||||
|
'reseller_admin FROM account'):
|
||||||
|
cmd = ['swauth-add-user', '-K', super_admin_key, '-s',
|
||||||
|
cfaccount.split('_', 1)[1]]
|
||||||
|
if admin == 't':
|
||||||
|
cmd.append('-a')
|
||||||
|
if reseller_admin == 't':
|
||||||
|
cmd.append('-r')
|
||||||
|
cmd.extend([account, user, password])
|
||||||
|
print ' '.join(cmd)
|
||||||
|
call(cmd)
|
||||||
|
print '----------------------------------------------------------------'
|
||||||
|
print ' Assuming the above worked perfectly, you should copy and paste '
|
||||||
|
print ' those lines into your ~/bin/recreateaccounts script.'
|
||||||
|
print '----------------------------------------------------------------'
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -22,7 +22,7 @@ import uuid
|
|||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
from swift.common.bench import BenchController
|
from swift.common.bench import BenchController
|
||||||
from swift.common.utils import readconf, NamedLogger
|
from swift.common.utils import readconf, LogAdapter, NamedFormatter
|
||||||
|
|
||||||
# The defaults should be sufficient to run swift-bench on a SAIO
|
# The defaults should be sufficient to run swift-bench on a SAIO
|
||||||
CONF_DEFAULTS = {
|
CONF_DEFAULTS = {
|
||||||
@ -124,10 +124,11 @@ if __name__ == '__main__':
|
|||||||
'critical': logging.CRITICAL}.get(
|
'critical': logging.CRITICAL}.get(
|
||||||
options.log_level.lower(), logging.INFO))
|
options.log_level.lower(), logging.INFO))
|
||||||
loghandler = logging.StreamHandler()
|
loghandler = logging.StreamHandler()
|
||||||
logformat = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
|
||||||
loghandler.setFormatter(logformat)
|
|
||||||
logger.addHandler(loghandler)
|
logger.addHandler(loghandler)
|
||||||
logger = NamedLogger(logger, 'swift-bench')
|
logger = LogAdapter(logger)
|
||||||
|
logformat = NamedFormatter('swift-bench', logger,
|
||||||
|
fmt='%(server)s %(asctime)s %(levelname)s %(message)s')
|
||||||
|
loghandler.setFormatter(logformat)
|
||||||
|
|
||||||
controller = BenchController(logger, options)
|
controller = BenchController(logger, options)
|
||||||
controller.run()
|
controller.run()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python -uO
|
#!/usr/bin/python -uO
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -20,20 +20,40 @@ from gzip import GzipFile
|
|||||||
from os import mkdir
|
from os import mkdir
|
||||||
from os.path import basename, dirname, exists, join as pathjoin
|
from os.path import basename, dirname, exists, join as pathjoin
|
||||||
from sys import argv, exit
|
from sys import argv, exit
|
||||||
|
from textwrap import wrap
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from swift.common.ring import RingBuilder
|
from swift.common.ring import RingBuilder
|
||||||
|
|
||||||
|
|
||||||
MAJOR_VERSION = 1
|
MAJOR_VERSION = 1
|
||||||
MINOR_VERSION = 1
|
MINOR_VERSION = 2
|
||||||
EXIT_RING_CHANGED = 0
|
EXIT_RING_CHANGED = 0
|
||||||
EXIT_RING_UNCHANGED = 1
|
EXIT_RING_UNCHANGED = 1
|
||||||
EXIT_ERROR = 2
|
EXIT_ERROR = 2
|
||||||
|
|
||||||
|
|
||||||
def search_devs(builder, search_value):
|
def search_devs(builder, search_value):
|
||||||
# d<device_id>z<zone>-<ip>:<port>/<device_name>_<meta>
|
"""
|
||||||
|
The <search-value> can be of the form:
|
||||||
|
d<device_id>z<zone>-<ip>:<port>/<device_name>_<meta>
|
||||||
|
Any part is optional, but you must include at least one part.
|
||||||
|
Examples:
|
||||||
|
d74 Matches the device id 74
|
||||||
|
z1 Matches devices in zone 1
|
||||||
|
z1-1.2.3.4 Matches devices in zone 1 with the ip 1.2.3.4
|
||||||
|
1.2.3.4 Matches devices in any zone with the ip 1.2.3.4
|
||||||
|
z1:5678 Matches devices in zone 1 using port 5678
|
||||||
|
:5678 Matches devices that use port 5678
|
||||||
|
/sdb1 Matches devices with the device name sdb1
|
||||||
|
_shiny Matches devices with shiny in the meta data
|
||||||
|
_"snet: 5.6.7.8" Matches devices with snet: 5.6.7.8 in the meta data
|
||||||
|
Most specific example:
|
||||||
|
d74z1-1.2.3.4:5678/sdb1_"snet: 5.6.7.8"
|
||||||
|
Nerd explanation:
|
||||||
|
All items require their single character prefix except the ip, in which
|
||||||
|
case the - is optional unless the device id or zone is also included.
|
||||||
|
"""
|
||||||
orig_search_value = search_value
|
orig_search_value = search_value
|
||||||
match = []
|
match = []
|
||||||
if search_value.startswith('d'):
|
if search_value.startswith('d'):
|
||||||
@ -72,7 +92,8 @@ def search_devs(builder, search_value):
|
|||||||
match.append(('meta', search_value[1:]))
|
match.append(('meta', search_value[1:]))
|
||||||
search_value = ''
|
search_value = ''
|
||||||
if search_value:
|
if search_value:
|
||||||
raise ValueError('Invalid <search-value>: %s' % repr(orig_search_value))
|
raise ValueError('Invalid <search-value>: %s' %
|
||||||
|
repr(orig_search_value))
|
||||||
devs = []
|
devs = []
|
||||||
for dev in builder.devs:
|
for dev in builder.devs:
|
||||||
if not dev:
|
if not dev:
|
||||||
@ -89,142 +110,22 @@ def search_devs(builder, search_value):
|
|||||||
return devs
|
return devs
|
||||||
|
|
||||||
|
|
||||||
SEARCH_VALUE_HELP = '''
|
class Commands:
|
||||||
The <search-value> can be of the form:
|
|
||||||
d<device_id>z<zone>-<ip>:<port>/<device_name>_<meta>
|
|
||||||
Any part is optional, but you must include at least one part.
|
|
||||||
Examples:
|
|
||||||
d74 Matches the device id 74
|
|
||||||
z1 Matches devices in zone 1
|
|
||||||
z1-1.2.3.4 Matches devices in zone 1 with the ip 1.2.3.4
|
|
||||||
1.2.3.4 Matches devices in any zone with the ip 1.2.3.4
|
|
||||||
z1:5678 Matches devices in zone 1 using port 5678
|
|
||||||
:5678 Matches devices that use port 5678
|
|
||||||
/sdb1 Matches devices with the device name sdb1
|
|
||||||
_shiny Matches devices with shiny in the meta data
|
|
||||||
_"snet: 5.6.7.8" Matches devices with snet: 5.6.7.8 in the meta data
|
|
||||||
Most specific example:
|
|
||||||
d74z1-1.2.3.4:5678/sdb1_"snet: 5.6.7.8"
|
|
||||||
Nerd explanation:
|
|
||||||
All items require their single character prefix except the ip, in which
|
|
||||||
case the - is optional unless the device id or zone is also included.
|
|
||||||
'''.strip()
|
|
||||||
|
|
||||||
CREATE_HELP = '''
|
def unknown():
|
||||||
swift-ring-builder <builder_file> create <part_power> <replicas> <min_part_hours>
|
print 'Unknown command: %s' % argv[2]
|
||||||
|
exit(EXIT_ERROR)
|
||||||
|
|
||||||
|
def create():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> create <part_power> <replicas>
|
||||||
|
<min_part_hours>
|
||||||
Creates <builder_file> with 2^<part_power> partitions and <replicas>.
|
Creates <builder_file> with 2^<part_power> partitions and <replicas>.
|
||||||
<min_part_hours> is number of hours to restrict moving a partition more
|
<min_part_hours> is number of hours to restrict moving a partition more
|
||||||
than once.
|
than once.
|
||||||
'''.strip()
|
"""
|
||||||
|
|
||||||
SEARCH_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> search <search-value>
|
|
||||||
Shows information about matching devices.
|
|
||||||
|
|
||||||
%(SEARCH_VALUE_HELP)s
|
|
||||||
'''.strip() % globals()
|
|
||||||
|
|
||||||
ADD_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> add z<zone>-<ip>:<port>/<device_name>_<meta> <wght>
|
|
||||||
Adds a device to the ring with the given information. No partitions will be
|
|
||||||
assigned to the new device until after running 'rebalance'. This is so you
|
|
||||||
can make multiple device changes and rebalance them all just once.
|
|
||||||
'''.strip()
|
|
||||||
|
|
||||||
SET_WEIGHT_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> set_weight <search-value> <weight>
|
|
||||||
Resets the device's weight. No partitions will be reassigned to or from the
|
|
||||||
device until after running 'rebalance'. This is so you can make multiple
|
|
||||||
device changes and rebalance them all just once.
|
|
||||||
|
|
||||||
%(SEARCH_VALUE_HELP)s
|
|
||||||
'''.strip() % globals()
|
|
||||||
|
|
||||||
SET_INFO_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> set_info <search-value>
|
|
||||||
<ip>:<port>/<device_name>_<meta>
|
|
||||||
Resets the device's information. This information isn't used to assign
|
|
||||||
partitions, so you can use 'write_ring' afterward to rewrite the current
|
|
||||||
ring with the newer device information. Any of the parts are optional
|
|
||||||
in the final <ip>:<port>/<device_name>_<meta> parameter; just give what you
|
|
||||||
want to change. For instance set_info d74 _"snet: 5.6.7.8" would just
|
|
||||||
update the meta data for device id 74.
|
|
||||||
|
|
||||||
%(SEARCH_VALUE_HELP)s
|
|
||||||
'''.strip() % globals()
|
|
||||||
|
|
||||||
REMOVE_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> remove <search-value>
|
|
||||||
Removes the device(s) from the ring. This should normally just be used for
|
|
||||||
a device that has failed. For a device you wish to decommission, it's best
|
|
||||||
to set its weight to 0, wait for it to drain all its data, then use this
|
|
||||||
remove command. This will not take effect until after running 'rebalance'.
|
|
||||||
This is so you can make multiple device changes and rebalance them all just
|
|
||||||
once.
|
|
||||||
|
|
||||||
%(SEARCH_VALUE_HELP)s
|
|
||||||
'''.strip() % globals()
|
|
||||||
|
|
||||||
SET_MIN_PART_HOURS_HELP = '''
|
|
||||||
swift-ring-builder <builder_file> set_min_part_hours <hours>
|
|
||||||
Changes the <min_part_hours> to the given <hours>. This should be set to
|
|
||||||
however long a full replication/update cycle takes. We're working on a way
|
|
||||||
to determine this more easily than scanning logs.
|
|
||||||
'''.strip()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if len(argv) < 2:
|
|
||||||
print '''
|
|
||||||
swift-ring-builder %(MAJOR_VERSION)s.%(MINOR_VERSION)s
|
|
||||||
|
|
||||||
%(CREATE_HELP)s
|
|
||||||
|
|
||||||
swift-ring-builder <builder_file>
|
|
||||||
Shows information about the ring and the devices within.
|
|
||||||
|
|
||||||
%(SEARCH_HELP)s
|
|
||||||
|
|
||||||
%(ADD_HELP)s
|
|
||||||
|
|
||||||
%(SET_WEIGHT_HELP)s
|
|
||||||
|
|
||||||
%(SET_INFO_HELP)s
|
|
||||||
|
|
||||||
%(REMOVE_HELP)s
|
|
||||||
|
|
||||||
swift-ring-builder <builder_file> rebalance
|
|
||||||
Attempts to rebalance the ring by reassigning partitions that haven't been
|
|
||||||
recently reassigned.
|
|
||||||
|
|
||||||
swift-ring-builder <builder_file> validate
|
|
||||||
Just runs the validation routines on the ring.
|
|
||||||
|
|
||||||
swift-ring-builder <builder_file> write_ring
|
|
||||||
Just rewrites the distributable ring file. This is done automatically after
|
|
||||||
a successful rebalance, so really this is only useful after one or more
|
|
||||||
'set_info' calls when no rebalance is needed but you want to send out the
|
|
||||||
new device information.
|
|
||||||
|
|
||||||
%(SET_MIN_PART_HOURS_HELP)s
|
|
||||||
|
|
||||||
Quick list: create search add set_weight set_info remove rebalance write_ring
|
|
||||||
set_min_part_hours
|
|
||||||
Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|
||||||
'''.strip() % globals()
|
|
||||||
exit(EXIT_RING_UNCHANGED)
|
|
||||||
|
|
||||||
if exists(argv[1]):
|
|
||||||
builder = pickle.load(open(argv[1], 'rb'))
|
|
||||||
for dev in builder.devs:
|
|
||||||
if dev and 'meta' not in dev:
|
|
||||||
dev['meta'] = ''
|
|
||||||
elif len(argv) < 3 or argv[2] != 'create':
|
|
||||||
print 'Ring Builder file does not exist: %s' % argv[1]
|
|
||||||
exit(EXIT_ERROR)
|
|
||||||
elif argv[2] == 'create':
|
|
||||||
if len(argv) < 6:
|
if len(argv) < 6:
|
||||||
print CREATE_HELP
|
print Commands.create.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
builder = RingBuilder(int(argv[3]), int(argv[4]), int(argv[5]))
|
builder = RingBuilder(int(argv[3]), int(argv[4]), int(argv[5]))
|
||||||
backup_dir = pathjoin(dirname(argv[1]), 'backups')
|
backup_dir = pathjoin(dirname(argv[1]), 'backups')
|
||||||
@ -238,19 +139,11 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_CHANGED)
|
exit(EXIT_RING_CHANGED)
|
||||||
|
|
||||||
backup_dir = pathjoin(dirname(argv[1]), 'backups')
|
def default():
|
||||||
try:
|
"""
|
||||||
mkdir(backup_dir)
|
swift-ring-builder <builder_file>
|
||||||
except OSError, err:
|
Shows information about the ring and the devices within.
|
||||||
if err.errno != EEXIST:
|
"""
|
||||||
raise
|
|
||||||
|
|
||||||
ring_file = argv[1]
|
|
||||||
if ring_file.endswith('.builder'):
|
|
||||||
ring_file = ring_file[:-len('.builder')]
|
|
||||||
ring_file += '.ring.gz'
|
|
||||||
|
|
||||||
if len(argv) == 2:
|
|
||||||
print '%s, build version %d' % (argv[1], builder.version)
|
print '%s, build version %d' % (argv[1], builder.version)
|
||||||
zones = 0
|
zones = 0
|
||||||
balance = 0
|
balance = 0
|
||||||
@ -284,9 +177,15 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
dev['meta'])
|
dev['meta'])
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
if argv[2] == 'search':
|
def search():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> search <search-value>
|
||||||
|
Shows information about matching devices.
|
||||||
|
"""
|
||||||
if len(argv) < 4:
|
if len(argv) < 4:
|
||||||
print SEARCH_HELP
|
print Commands.search.__doc__.strip()
|
||||||
|
print
|
||||||
|
print search_devs.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
devs = search_devs(builder, argv[3])
|
devs = search_devs(builder, argv[3])
|
||||||
if not devs:
|
if not devs:
|
||||||
@ -311,10 +210,16 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
dev['meta'])
|
dev['meta'])
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'add':
|
def add():
|
||||||
# add z<zone>-<ip>:<port>/<device_name>_<meta> <wght>
|
"""
|
||||||
|
swift-ring-builder <builder_file> add z<zone>-<ip>:<port>/<device_name>_<meta>
|
||||||
|
<wght>
|
||||||
|
Adds a device to the ring with the given information. No partitions will be
|
||||||
|
assigned to the new device until after running 'rebalance'. This is so you
|
||||||
|
can make multiple device changes and rebalance them all just once.
|
||||||
|
"""
|
||||||
if len(argv) < 5:
|
if len(argv) < 5:
|
||||||
print ADD_HELP
|
print Commands.add.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
if not argv[3].startswith('z'):
|
if not argv[3].startswith('z'):
|
||||||
@ -379,9 +284,17 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'set_weight':
|
def set_weight():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> set_weight <search-value> <weight>
|
||||||
|
Resets the device's weight. No partitions will be reassigned to or from the
|
||||||
|
device until after running 'rebalance'. This is so you can make multiple
|
||||||
|
device changes and rebalance them all just once.
|
||||||
|
"""
|
||||||
if len(argv) != 5:
|
if len(argv) != 5:
|
||||||
print SET_WEIGHT_HELP
|
print Commands.set_weight.__doc__.strip()
|
||||||
|
print
|
||||||
|
print search_devs.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
devs = search_devs(builder, argv[3])
|
devs = search_devs(builder, argv[3])
|
||||||
weight = float(argv[4])
|
weight = float(argv[4])
|
||||||
@ -404,9 +317,21 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'set_info':
|
def set_info():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> set_info <search-value>
|
||||||
|
<ip>:<port>/<device_name>_<meta>
|
||||||
|
Resets the device's information. This information isn't used to assign
|
||||||
|
partitions, so you can use 'write_ring' afterward to rewrite the current
|
||||||
|
ring with the newer device information. Any of the parts are optional
|
||||||
|
in the final <ip>:<port>/<device_name>_<meta> parameter; just give what you
|
||||||
|
want to change. For instance set_info d74 _"snet: 5.6.7.8" would just
|
||||||
|
update the meta data for device id 74.
|
||||||
|
"""
|
||||||
if len(argv) != 5:
|
if len(argv) != 5:
|
||||||
print SET_INFO_HELP
|
print Commands.set_info.__doc__.strip()
|
||||||
|
print
|
||||||
|
print search_devs.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
devs = search_devs(builder, argv[3])
|
devs = search_devs(builder, argv[3])
|
||||||
change_value = argv[4]
|
change_value = argv[4]
|
||||||
@ -471,9 +396,20 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'remove':
|
def remove():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> remove <search-value>
|
||||||
|
Removes the device(s) from the ring. This should normally just be used for
|
||||||
|
a device that has failed. For a device you wish to decommission, it's best
|
||||||
|
to set its weight to 0, wait for it to drain all its data, then use this
|
||||||
|
remove command. This will not take effect until after running 'rebalance'.
|
||||||
|
This is so you can make multiple device changes and rebalance them all just
|
||||||
|
once.
|
||||||
|
"""
|
||||||
if len(argv) < 4:
|
if len(argv) < 4:
|
||||||
print REMOVE_HELP
|
print Commands.remove.__doc__.strip()
|
||||||
|
print
|
||||||
|
print search_devs.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
devs = search_devs(builder, argv[3])
|
devs = search_devs(builder, argv[3])
|
||||||
if not devs:
|
if not devs:
|
||||||
@ -491,11 +427,17 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
for dev in devs:
|
for dev in devs:
|
||||||
builder.remove_dev(dev['id'])
|
builder.remove_dev(dev['id'])
|
||||||
print 'd%(id)sz%(zone)s-%(ip)s:%(port)s/%(device)s_"%(meta)s" ' \
|
print 'd%(id)sz%(zone)s-%(ip)s:%(port)s/%(device)s_"%(meta)s" ' \
|
||||||
'marked for removal and will be removed next rebalance.' % dev
|
'marked for removal and will be removed next rebalance.' \
|
||||||
|
% dev
|
||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'rebalance':
|
def rebalance():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> rebalance
|
||||||
|
Attempts to rebalance the ring by reassigning partitions that haven't been
|
||||||
|
recently reassigned.
|
||||||
|
"""
|
||||||
devs_changed = builder.devs_changed
|
devs_changed = builder.devs_changed
|
||||||
last_balance = builder.get_balance()
|
last_balance = builder.get_balance()
|
||||||
parts, balance = builder.rebalance()
|
parts, balance = builder.rebalance()
|
||||||
@ -528,31 +470,50 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_CHANGED)
|
exit(EXIT_RING_CHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'validate':
|
def validate():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> validate
|
||||||
|
Just runs the validation routines on the ring.
|
||||||
|
"""
|
||||||
builder.validate()
|
builder.validate()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'write_ring':
|
def write_ring():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> write_ring
|
||||||
|
Just rewrites the distributable ring file. This is done automatically after
|
||||||
|
a successful rebalance, so really this is only useful after one or more
|
||||||
|
'set_info' calls when no rebalance is needed but you want to send out the
|
||||||
|
new device information.
|
||||||
|
"""
|
||||||
ring_data = builder.get_ring()
|
ring_data = builder.get_ring()
|
||||||
if not ring_data._replica2part2dev_id:
|
if not ring_data._replica2part2dev_id:
|
||||||
if ring_data.devs:
|
if ring_data.devs:
|
||||||
print 'Warning: Writing a ring with no partition assignments but with devices; did you forget to run "rebalance"?'
|
print 'Warning: Writing a ring with no partition ' \
|
||||||
else:
|
'assignments but with devices; did you forget to run ' \
|
||||||
print 'Warning: Writing an empty ring'
|
'"rebalance"?'
|
||||||
|
else:
|
||||||
|
print 'Warning: Writing an empty ring'
|
||||||
pickle.dump(ring_data,
|
pickle.dump(ring_data,
|
||||||
GzipFile(pathjoin(backup_dir, '%d.' % time() +
|
GzipFile(pathjoin(backup_dir, '%d.' % time() +
|
||||||
basename(ring_file)), 'wb'), protocol=2)
|
basename(ring_file)), 'wb'), protocol=2)
|
||||||
pickle.dump(ring_data, GzipFile(ring_file, 'wb'), protocol=2)
|
pickle.dump(ring_data, GzipFile(ring_file, 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_CHANGED)
|
exit(EXIT_RING_CHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'pretend_min_part_hours_passed':
|
def pretend_min_part_hours_passed():
|
||||||
builder.pretend_min_part_hours_passed()
|
builder.pretend_min_part_hours_passed()
|
||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
elif argv[2] == 'set_min_part_hours':
|
def set_min_part_hours():
|
||||||
|
"""
|
||||||
|
swift-ring-builder <builder_file> set_min_part_hours <hours>
|
||||||
|
Changes the <min_part_hours> to the given <hours>. This should be set to
|
||||||
|
however long a full replication/update cycle takes. We're working on a way
|
||||||
|
to determine this more easily than scanning logs.
|
||||||
|
"""
|
||||||
if len(argv) < 4:
|
if len(argv) < 4:
|
||||||
print SET_MIN_PART_HOURS_HELP
|
print Commands.set_min_part_hours.__doc__.strip()
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
builder.change_min_part_hours(int(argv[3]))
|
builder.change_min_part_hours(int(argv[3]))
|
||||||
print 'The minimum number of hours before a partition can be ' \
|
print 'The minimum number of hours before a partition can be ' \
|
||||||
@ -560,5 +521,51 @@ Exit codes: 0 = ring changed, 1 = ring did not change, 2 = error
|
|||||||
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
pickle.dump(builder, open(argv[1], 'wb'), protocol=2)
|
||||||
exit(EXIT_RING_UNCHANGED)
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
print 'Unknown command: %s' % argv[2]
|
|
||||||
exit(EXIT_ERROR)
|
if __name__ == '__main__':
|
||||||
|
if len(argv) < 2:
|
||||||
|
print "swift-ring-builder %(MAJOR_VERSION)s.%(MINOR_VERSION)s\n" % \
|
||||||
|
globals()
|
||||||
|
print Commands.default.__doc__.strip()
|
||||||
|
print
|
||||||
|
cmds = [c for c, f in Commands.__dict__.iteritems()
|
||||||
|
if f.__doc__ and c[0] != '_' and c != 'default']
|
||||||
|
cmds.sort()
|
||||||
|
for cmd in cmds:
|
||||||
|
print Commands.__dict__[cmd].__doc__.strip()
|
||||||
|
print
|
||||||
|
print search_devs.__doc__.strip()
|
||||||
|
print
|
||||||
|
for line in wrap(' '.join(cmds), 79, initial_indent='Quick list: ',
|
||||||
|
subsequent_indent=' '):
|
||||||
|
print line
|
||||||
|
print 'Exit codes: 0 = ring changed, 1 = ring did not change, ' \
|
||||||
|
'2 = error'
|
||||||
|
exit(EXIT_RING_UNCHANGED)
|
||||||
|
|
||||||
|
if exists(argv[1]):
|
||||||
|
builder = pickle.load(open(argv[1], 'rb'))
|
||||||
|
for dev in builder.devs:
|
||||||
|
if dev and 'meta' not in dev:
|
||||||
|
dev['meta'] = ''
|
||||||
|
elif len(argv) < 3 or argv[2] != 'create':
|
||||||
|
print 'Ring Builder file does not exist: %s' % argv[1]
|
||||||
|
exit(EXIT_ERROR)
|
||||||
|
|
||||||
|
backup_dir = pathjoin(dirname(argv[1]), 'backups')
|
||||||
|
try:
|
||||||
|
mkdir(backup_dir)
|
||||||
|
except OSError, err:
|
||||||
|
if err.errno != EEXIST:
|
||||||
|
raise
|
||||||
|
|
||||||
|
ring_file = argv[1]
|
||||||
|
if ring_file.endswith('.builder'):
|
||||||
|
ring_file = ring_file[:-len('.builder')]
|
||||||
|
ring_file += '.ring.gz'
|
||||||
|
|
||||||
|
if len(argv) == 2:
|
||||||
|
command = "default"
|
||||||
|
else:
|
||||||
|
command = argv[2]
|
||||||
|
Commands.__dict__.get(command, Commands.unknown)()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/python -u
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python -u
|
#!/usr/bin/python -u
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -164,7 +164,10 @@ swift-stats-populate and swift-stats-report use the same configuration file,
|
|||||||
/etc/swift/stats.conf. Example conf file::
|
/etc/swift/stats.conf. Example conf file::
|
||||||
|
|
||||||
[stats]
|
[stats]
|
||||||
|
# For DevAuth:
|
||||||
auth_url = http://saio:11000/v1.0
|
auth_url = http://saio:11000/v1.0
|
||||||
|
# For Swauth:
|
||||||
|
# auth_url = http://saio:11000/auth/v1.0
|
||||||
auth_user = test:tester
|
auth_user = test:tester
|
||||||
auth_key = testing
|
auth_key = testing
|
||||||
|
|
||||||
@ -229,6 +232,21 @@ get performance timings (warning: the initial populate takes a while). These
|
|||||||
timings are dumped into a CSV file (/etc/swift/stats.csv by default) and can
|
timings are dumped into a CSV file (/etc/swift/stats.csv by default) and can
|
||||||
then be graphed to see how cluster performance is trending.
|
then be graphed to see how cluster performance is trending.
|
||||||
|
|
||||||
|
------------------------------------
|
||||||
|
Additional Cleanup Script for Swauth
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
If you decide to use Swauth, you'll want to install a cronjob to clean up any
|
||||||
|
orphaned expired tokens. These orphaned tokens can occur when a "stampede"
|
||||||
|
occurs where a single user authenticates several times concurrently. Generally,
|
||||||
|
these orphaned tokens don't pose much of an issue, but it's good to clean them
|
||||||
|
up once a "token life" period (default: 1 day or 86400 seconds).
|
||||||
|
|
||||||
|
This should be as simple as adding `swauth-cleanup-tokens -K swauthkey >
|
||||||
|
/dev/null` to a crontab entry on one of the proxies that is running Swauth; but
|
||||||
|
run `swauth-cleanup-tokens` with no arguments for detailed help on the options
|
||||||
|
available.
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
Debugging Tips and Tools
|
Debugging Tips and Tools
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -229,7 +229,12 @@ Option Default Description
|
|||||||
log_name object-auditor Label used when logging
|
log_name object-auditor Label used when logging
|
||||||
log_facility LOG_LOCAL0 Syslog log facility
|
log_facility LOG_LOCAL0 Syslog log facility
|
||||||
log_level INFO Logging level
|
log_level INFO Logging level
|
||||||
interval 1800 Minimum time for a pass to take
|
files_per_second 20 Maximum files audited per second. Should
|
||||||
|
be tuned according to individual system
|
||||||
|
specs. 0 is unlimited.
|
||||||
|
bytes_per_second 10000000 Maximum bytes audited per second. Should
|
||||||
|
be tuned according to individual system
|
||||||
|
specs. 0 is unlimited.
|
||||||
================== ============== ==========================================
|
================== ============== ==========================================
|
||||||
|
|
||||||
------------------------------
|
------------------------------
|
||||||
@ -484,6 +489,43 @@ ssl False If True, use SSL to
|
|||||||
node_timeout 10 Request timeout
|
node_timeout 10 Request timeout
|
||||||
============ =================================== ========================
|
============ =================================== ========================
|
||||||
|
|
||||||
|
[swauth]
|
||||||
|
|
||||||
|
===================== =============================== =======================
|
||||||
|
Option Default Description
|
||||||
|
--------------------- ------------------------------- -----------------------
|
||||||
|
use Entry point for
|
||||||
|
paste.deploy to use for
|
||||||
|
auth. To use the swauth
|
||||||
|
set to:
|
||||||
|
`egg:swift#swauth`
|
||||||
|
log_name auth-server Label used when logging
|
||||||
|
log_facility LOG_LOCAL0 Syslog log facility
|
||||||
|
log_level INFO Log level
|
||||||
|
log_headers True If True, log headers in
|
||||||
|
each request
|
||||||
|
reseller_prefix AUTH The naming scope for the
|
||||||
|
auth service. Swift
|
||||||
|
storage accounts and
|
||||||
|
auth tokens will begin
|
||||||
|
with this prefix.
|
||||||
|
auth_prefix /auth/ The HTTP request path
|
||||||
|
prefix for the auth
|
||||||
|
service. Swift itself
|
||||||
|
reserves anything
|
||||||
|
beginning with the
|
||||||
|
letter `v`.
|
||||||
|
default_swift_cluster local:http://127.0.0.1:8080/v1 The default Swift
|
||||||
|
cluster to place newly
|
||||||
|
created accounts on.
|
||||||
|
token_life 86400 The number of seconds a
|
||||||
|
token is valid.
|
||||||
|
node_timeout 10 Request timeout
|
||||||
|
super_admin_key None The key for the
|
||||||
|
.super_admin account.
|
||||||
|
===================== =============================== =======================
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
Memcached Considerations
|
Memcached Considerations
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -8,7 +8,7 @@ Creating Your Own Auth Server and Middleware
|
|||||||
|
|
||||||
The included swift/auth/server.py and swift/common/middleware/auth.py are good
|
The included swift/auth/server.py and swift/common/middleware/auth.py are good
|
||||||
minimal examples of how to create an external auth server and proxy server auth
|
minimal examples of how to create an external auth server and proxy server auth
|
||||||
middleware. Also, see the `Swauth <https://launchpad.net/swauth>`_ project for
|
middleware. Also, see swift/common/middleware/swauth.py for
|
||||||
a more complete implementation. The main points are that the auth middleware
|
a more complete implementation. The main points are that the auth middleware
|
||||||
can reject requests up front, before they ever get to the Swift Proxy
|
can reject requests up front, before they ever get to the Swift Proxy
|
||||||
application, and afterwards when the proxy issues callbacks to verify
|
application, and afterwards when the proxy issues callbacks to verify
|
||||||
@ -356,6 +356,7 @@ repoze.what::
|
|||||||
self.auth_port = int(conf.get('port', 11000))
|
self.auth_port = int(conf.get('port', 11000))
|
||||||
self.ssl = \
|
self.ssl = \
|
||||||
conf.get('ssl', 'false').lower() in ('true', 'on', '1', 'yes')
|
conf.get('ssl', 'false').lower() in ('true', 'on', '1', 'yes')
|
||||||
|
self.auth_prefix = conf.get('prefix', '/')
|
||||||
self.timeout = int(conf.get('node_timeout', 10))
|
self.timeout = int(conf.get('node_timeout', 10))
|
||||||
|
|
||||||
def authenticate(self, env, identity):
|
def authenticate(self, env, identity):
|
||||||
@ -371,7 +372,7 @@ repoze.what::
|
|||||||
return user
|
return user
|
||||||
with Timeout(self.timeout):
|
with Timeout(self.timeout):
|
||||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
||||||
'/token/%s' % token, ssl=self.ssl)
|
'%stoken/%s' % (self.auth_prefix, token), ssl=self.ssl)
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
resp.read()
|
resp.read()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -38,7 +38,7 @@ License and Copyright
|
|||||||
Every source file should have the following copyright and license statement at
|
Every source file should have the following copyright and license statement at
|
||||||
the top::
|
the top::
|
||||||
|
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -216,7 +216,9 @@ Configuring each node
|
|||||||
|
|
||||||
Sample configuration files are provided with all defaults in line-by-line comments.
|
Sample configuration files are provided with all defaults in line-by-line comments.
|
||||||
|
|
||||||
#. Create `/etc/swift/auth-server.conf`::
|
#. If your going to use the DevAuth (the default swift-auth-server), create
|
||||||
|
`/etc/swift/auth-server.conf` (you can skip this if you're going to use
|
||||||
|
Swauth)::
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
user = <your-user-name>
|
user = <your-user-name>
|
||||||
@ -237,15 +239,25 @@ Sample configuration files are provided with all defaults in line-by-line commen
|
|||||||
user = <your-user-name>
|
user = <your-user-name>
|
||||||
|
|
||||||
[pipeline:main]
|
[pipeline:main]
|
||||||
|
# For DevAuth:
|
||||||
pipeline = healthcheck cache auth proxy-server
|
pipeline = healthcheck cache auth proxy-server
|
||||||
|
# For Swauth:
|
||||||
|
# pipeline = healthcheck cache swauth proxy-server
|
||||||
|
|
||||||
[app:proxy-server]
|
[app:proxy-server]
|
||||||
use = egg:swift#proxy
|
use = egg:swift#proxy
|
||||||
allow_account_management = true
|
allow_account_management = true
|
||||||
|
|
||||||
|
# Only needed for DevAuth
|
||||||
[filter:auth]
|
[filter:auth]
|
||||||
use = egg:swift#auth
|
use = egg:swift#auth
|
||||||
|
|
||||||
|
# Only needed for Swauth
|
||||||
|
[filter:swauth]
|
||||||
|
use = egg:swift#swauth
|
||||||
|
# Highly recommended to change this.
|
||||||
|
super_admin_key = swauthkey
|
||||||
|
|
||||||
[filter:healthcheck]
|
[filter:healthcheck]
|
||||||
use = egg:swift#healthcheck
|
use = egg:swift#healthcheck
|
||||||
|
|
||||||
@ -562,18 +574,32 @@ Setting up scripts for running Swift
|
|||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# The auth-server line is only needed for DevAuth:
|
||||||
swift-init auth-server start
|
swift-init auth-server start
|
||||||
swift-init proxy-server start
|
swift-init proxy-server start
|
||||||
swift-init account-server start
|
swift-init account-server start
|
||||||
swift-init container-server start
|
swift-init container-server start
|
||||||
swift-init object-server start
|
swift-init object-server start
|
||||||
|
|
||||||
|
#. For Swauth (not needed for DevAuth), create `~/bin/recreateaccounts`::
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Replace devauth with whatever your super_admin key is (recorded in
|
||||||
|
# /etc/swift/proxy-server.conf).
|
||||||
|
swauth-prep -K swauthkey
|
||||||
|
swauth-add-user -K swauthkey -a test tester testing
|
||||||
|
swauth-add-user -K swauthkey -a test2 tester2 testing2
|
||||||
|
swauth-add-user -K swauthkey test tester3 testing3
|
||||||
|
swauth-add-user -K swauthkey -a -r reseller reseller reseller
|
||||||
|
|
||||||
#. Create `~/bin/startrest`::
|
#. Create `~/bin/startrest`::
|
||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Replace devauth with whatever your super_admin key is (recorded in
|
# Replace devauth with whatever your super_admin key is (recorded in
|
||||||
# /etc/swift/auth-server.conf).
|
# /etc/swift/auth-server.conf). This swift-auth-recreate-accounts line
|
||||||
|
# is only needed for DevAuth:
|
||||||
swift-auth-recreate-accounts -K devauth
|
swift-auth-recreate-accounts -K devauth
|
||||||
swift-init object-updater start
|
swift-init object-updater start
|
||||||
swift-init container-updater start
|
swift-init container-updater start
|
||||||
@ -589,13 +615,14 @@ Setting up scripts for running Swift
|
|||||||
#. `remakerings`
|
#. `remakerings`
|
||||||
#. `cd ~/swift/trunk; ./.unittests`
|
#. `cd ~/swift/trunk; ./.unittests`
|
||||||
#. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.)
|
#. `startmain` (The ``Unable to increase file descriptor limit. Running as non-root?`` warnings are expected and ok.)
|
||||||
#. `swift-auth-add-user -K devauth -a test tester testing` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
#. For Swauth: `recreateaccounts`
|
||||||
#. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:11000/v1.0``
|
#. For DevAuth: `swift-auth-add-user -K devauth -a test tester testing` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
||||||
|
#. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:11000/v1.0`` # For Swauth, make the last URL `http://127.0.0.1:8080/auth/v1.0`
|
||||||
#. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>``
|
#. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>``
|
||||||
#. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat`
|
#. Check that `st` works: `st -A http://127.0.0.1:11000/v1.0 -U test:tester -K testing stat` # For Swauth, make the URL `http://127.0.0.1:8080/auth/v1.0`
|
||||||
#. `swift-auth-add-user -K devauth -a test2 tester2 testing2` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
#. For DevAuth: `swift-auth-add-user -K devauth -a test2 tester2 testing2` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
||||||
#. `swift-auth-add-user -K devauth test tester3 testing3` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
#. For DevAuth: `swift-auth-add-user -K devauth test tester3 testing3` # Replace ``devauth`` with whatever your super_admin key is (recorded in /etc/swift/auth-server.conf).
|
||||||
#. `cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf`
|
#. `cp ~/swift/trunk/test/functional/sample.conf /etc/swift/func_test.conf` # For Swauth, add auth_prefix = /auth/ and change auth_port = 8080.
|
||||||
#. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete
|
#. `cd ~/swift/trunk; ./.functests` (Note: functional tests will first delete
|
||||||
everything in the configured accounts.)
|
everything in the configured accounts.)
|
||||||
#. `cd ~/swift/trunk; ./.probetests` (Note: probe tests will reset your
|
#. `cd ~/swift/trunk; ./.probetests` (Note: probe tests will reset your
|
||||||
|
@ -8,7 +8,9 @@ Talking to Swift with Cyberduck
|
|||||||
|
|
||||||
#. Install Swift, or have credentials for an existing Swift installation. If
|
#. Install Swift, or have credentials for an existing Swift installation. If
|
||||||
you plan to install Swift on your own server, follow the general guidelines
|
you plan to install Swift on your own server, follow the general guidelines
|
||||||
in the section following this one.
|
in the section following this one. (This documentation assumes the use of
|
||||||
|
the DevAuth auth server; if you're using Swauth, you should change all auth
|
||||||
|
URLs /v1.0 to /auth/v1.0)
|
||||||
|
|
||||||
#. Verify you can connect using the standard Swift Tool `st` from your
|
#. Verify you can connect using the standard Swift Tool `st` from your
|
||||||
"public" URL (yes I know this resolves privately inside EC2)::
|
"public" URL (yes I know this resolves privately inside EC2)::
|
||||||
|
@ -13,8 +13,8 @@ Prerequisites
|
|||||||
Basic architecture and terms
|
Basic architecture and terms
|
||||||
----------------------------
|
----------------------------
|
||||||
- *node* - a host machine running one or more Swift services
|
- *node* - a host machine running one or more Swift services
|
||||||
- *Proxy node* - node that runs Proxy services
|
- *Proxy node* - node that runs Proxy services; can also run Swauth
|
||||||
- *Auth node* - node that runs the Auth service
|
- *Auth node* - node that runs the Auth service; only required for DevAuth
|
||||||
- *Storage node* - node that runs Account, Container, and Object services
|
- *Storage node* - node that runs Account, Container, and Object services
|
||||||
- *ring* - a set of mappings of Swift data to physical devices
|
- *ring* - a set of mappings of Swift data to physical devices
|
||||||
|
|
||||||
@ -23,13 +23,14 @@ This document shows a cluster using the following types of nodes:
|
|||||||
- one Proxy node
|
- one Proxy node
|
||||||
|
|
||||||
- Runs the swift-proxy-server processes which proxy requests to the
|
- Runs the swift-proxy-server processes which proxy requests to the
|
||||||
appropriate Storage nodes.
|
appropriate Storage nodes. For Swauth, the proxy server will also contain
|
||||||
|
the Swauth service as WSGI middleware.
|
||||||
|
|
||||||
- one Auth node
|
- one Auth node
|
||||||
|
|
||||||
- Runs the swift-auth-server which controls authentication and
|
- Runs the swift-auth-server which controls authentication and
|
||||||
authorization for all requests. This can be on the same node as a
|
authorization for all requests. This can be on the same node as a
|
||||||
Proxy node.
|
Proxy node. This is only required for DevAuth.
|
||||||
|
|
||||||
- five Storage nodes
|
- five Storage nodes
|
||||||
|
|
||||||
@ -120,16 +121,27 @@ Configure the Proxy node
|
|||||||
user = swift
|
user = swift
|
||||||
|
|
||||||
[pipeline:main]
|
[pipeline:main]
|
||||||
|
# For DevAuth:
|
||||||
pipeline = healthcheck cache auth proxy-server
|
pipeline = healthcheck cache auth proxy-server
|
||||||
|
# For Swauth:
|
||||||
|
# pipeline = healthcheck cache swauth proxy-server
|
||||||
|
|
||||||
[app:proxy-server]
|
[app:proxy-server]
|
||||||
use = egg:swift#proxy
|
use = egg:swift#proxy
|
||||||
allow_account_management = true
|
allow_account_management = true
|
||||||
|
|
||||||
|
# Only needed for DevAuth
|
||||||
[filter:auth]
|
[filter:auth]
|
||||||
use = egg:swift#auth
|
use = egg:swift#auth
|
||||||
ssl = true
|
ssl = true
|
||||||
|
|
||||||
|
# Only needed for Swauth
|
||||||
|
[filter:swauth]
|
||||||
|
use = egg:swift#swauth
|
||||||
|
default_swift_cluster = https://<PROXY_LOCAL_NET_IP>:8080/v1
|
||||||
|
# Highly recommended to change this key to something else!
|
||||||
|
super_admin_key = swauthkey
|
||||||
|
|
||||||
[filter:healthcheck]
|
[filter:healthcheck]
|
||||||
use = egg:swift#healthcheck
|
use = egg:swift#healthcheck
|
||||||
|
|
||||||
@ -194,6 +206,8 @@ Configure the Proxy node
|
|||||||
Configure the Auth node
|
Configure the Auth node
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
.. note:: Only required for DevAuth; you can skip this section for Swauth.
|
||||||
|
|
||||||
#. If this node is not running on the same node as a proxy, create a
|
#. If this node is not running on the same node as a proxy, create a
|
||||||
self-signed cert as you did for the Proxy node
|
self-signed cert as you did for the Proxy node
|
||||||
|
|
||||||
@ -358,13 +372,20 @@ Create Swift admin account and test
|
|||||||
|
|
||||||
You run these commands from the Auth node.
|
You run these commands from the Auth node.
|
||||||
|
|
||||||
|
.. note:: For Swauth, replace the https://<AUTH_HOSTNAME>:11000/v1.0 with
|
||||||
|
https://<PROXY_HOSTNAME>:8080/auth/v1.0
|
||||||
|
|
||||||
#. Create a user with administrative privileges (account = system,
|
#. Create a user with administrative privileges (account = system,
|
||||||
username = root, password = testpass). Make sure to replace
|
username = root, password = testpass). Make sure to replace
|
||||||
``devauth`` with whatever super_admin key you assigned in the
|
``devauth`` (or ``swauthkey``) with whatever super_admin key you assigned in
|
||||||
auth-server.conf file above. *Note: None of the values of
|
the auth-server.conf file (or proxy-server.conf file in the case of Swauth)
|
||||||
|
above. *Note: None of the values of
|
||||||
account, username, or password are special - they can be anything.*::
|
account, username, or password are special - they can be anything.*::
|
||||||
|
|
||||||
|
# For DevAuth:
|
||||||
swift-auth-add-user -K devauth -a system root testpass
|
swift-auth-add-user -K devauth -a system root testpass
|
||||||
|
# For Swauth:
|
||||||
|
swauth-add-user -K swauthkey -a system root testpass
|
||||||
|
|
||||||
#. Get an X-Storage-Url and X-Auth-Token::
|
#. Get an X-Storage-Url and X-Auth-Token::
|
||||||
|
|
||||||
@ -404,20 +425,50 @@ See :ref:`config-proxy` for the initial setup, and then follow these additional
|
|||||||
use = egg:swift#memcache
|
use = egg:swift#memcache
|
||||||
memcache_servers = <PROXY_LOCAL_NET_IP>:11211
|
memcache_servers = <PROXY_LOCAL_NET_IP>:11211
|
||||||
|
|
||||||
#. Change the default_cluster_url to point to the load balanced url, rather than the first proxy server you created in /etc/swift/auth-server.conf::
|
#. Change the default_cluster_url to point to the load balanced url, rather than the first proxy server you created in /etc/swift/auth-server.conf (for DevAuth) or in /etc/swift/proxy-server.conf (for Swauth)::
|
||||||
|
|
||||||
|
# For DevAuth, in /etc/swift/auth-server.conf
|
||||||
[app:auth-server]
|
[app:auth-server]
|
||||||
use = egg:swift#auth
|
use = egg:swift#auth
|
||||||
default_cluster_url = https://<LOAD_BALANCER_HOSTNAME>/v1
|
default_cluster_url = https://<LOAD_BALANCER_HOSTNAME>/v1
|
||||||
# Highly recommended to change this key to something else!
|
# Highly recommended to change this key to something else!
|
||||||
super_admin_key = devauth
|
super_admin_key = devauth
|
||||||
|
|
||||||
#. After you change the default_cluster_url setting, you have to delete the auth database and recreate the Swift users, or manually update the auth database with the correct URL for each account.
|
# For Swauth, in /etc/swift/proxy-server.conf
|
||||||
|
[filter:swauth]
|
||||||
|
use = egg:swift#swauth
|
||||||
|
default_swift_cluster = local:http://<LOAD_BALANCER_HOSTNAME>/v1
|
||||||
|
# Highly recommended to change this key to something else!
|
||||||
|
super_admin_key = swauthkey
|
||||||
|
|
||||||
|
#. For DevAuth, after you change the default_cluster_url setting, you have to delete the auth database and recreate the Swift users, or manually update the auth database with the correct URL for each account.
|
||||||
|
|
||||||
|
For Swauth, you can change a service URL with::
|
||||||
|
|
||||||
|
swauth-set-account-service -K swauthkey <account> storage local <new_url_for_the_account>
|
||||||
|
|
||||||
|
You can obtain old service URLs with::
|
||||||
|
|
||||||
|
swauth-list -K swauthkey <account>
|
||||||
|
|
||||||
#. Next, copy all the ring information to all the nodes, including your new proxy nodes, and ensure the ring info gets to all the storage nodes as well.
|
#. Next, copy all the ring information to all the nodes, including your new proxy nodes, and ensure the ring info gets to all the storage nodes as well.
|
||||||
|
|
||||||
#. After you sync all the nodes, make sure the admin has the keys in /etc/swift and the ownership for the ring file is correct.
|
#. After you sync all the nodes, make sure the admin has the keys in /etc/swift and the ownership for the ring file is correct.
|
||||||
|
|
||||||
|
Additional Cleanup Script for Swauth
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
If you decide to use Swauth, you'll want to install a cronjob to clean up any
|
||||||
|
orphaned expired tokens. These orphaned tokens can occur when a "stampede"
|
||||||
|
occurs where a single user authenticates several times concurrently. Generally,
|
||||||
|
these orphaned tokens don't pose much of an issue, but it's good to clean them
|
||||||
|
up once a "token life" period (default: 1 day or 86400 seconds).
|
||||||
|
|
||||||
|
This should be as simple as adding `swauth-cleanup-tokens -K swauthkey >
|
||||||
|
/dev/null` to a crontab entry on one of the proxies that is running Swauth; but
|
||||||
|
run `swauth-cleanup-tokens` with no arguments for detailed help on the options
|
||||||
|
available.
|
||||||
|
|
||||||
Troubleshooting Notes
|
Troubleshooting Notes
|
||||||
---------------------
|
---------------------
|
||||||
If you see problems, look in var/log/syslog (or messages on some distros).
|
If you see problems, look in var/log/syslog (or messages on some distros).
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
..
|
..
|
||||||
Copyright 2010 OpenStack LLC
|
Copyright 2010-2011 OpenStack LLC
|
||||||
All Rights Reserved.
|
All Rights Reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -42,6 +42,15 @@ Auth
|
|||||||
:members:
|
:members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. _common_swauth:
|
||||||
|
|
||||||
|
Swauth
|
||||||
|
======
|
||||||
|
|
||||||
|
.. automodule:: swift.common.middleware.swauth
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. _acls:
|
.. _acls:
|
||||||
|
|
||||||
ACLs
|
ACLs
|
||||||
|
@ -48,9 +48,148 @@ implementing your own auth.
|
|||||||
|
|
||||||
Also, see :doc:`development_auth`.
|
Also, see :doc:`development_auth`.
|
||||||
|
|
||||||
------------------
|
|
||||||
History and Future
|
|
||||||
------------------
|
|
||||||
|
|
||||||
What's established in Swift for authentication/authorization has history from
|
------
|
||||||
before Swift, so that won't be recorded here.
|
Swauth
|
||||||
|
------
|
||||||
|
|
||||||
|
The Swauth system is an optional DevAuth replacement included at
|
||||||
|
swift/common/middleware/swauth.py; a scalable authentication and
|
||||||
|
authorization system that uses Swift itself as its backing store. This section
|
||||||
|
will describe how it stores its data.
|
||||||
|
|
||||||
|
At the topmost level, the auth system has its own Swift account it stores its
|
||||||
|
own account information within. This Swift account is known as
|
||||||
|
self.auth_account in the code and its name is in the format
|
||||||
|
self.reseller_prefix + ".auth". In this text, we'll refer to this account as
|
||||||
|
<auth_account>.
|
||||||
|
|
||||||
|
The containers whose names do not begin with a period represent the accounts
|
||||||
|
within the auth service. For example, the <auth_account>/test container would
|
||||||
|
represent the "test" account.
|
||||||
|
|
||||||
|
The objects within each container represent the users for that auth service
|
||||||
|
account. For example, the <auth_account>/test/bob object would represent the
|
||||||
|
user "bob" within the auth service account of "test". Each of these user
|
||||||
|
objects contain a JSON dictionary of the format::
|
||||||
|
|
||||||
|
{"auth": "<auth_type>:<auth_value>", "groups": <groups_array>}
|
||||||
|
|
||||||
|
The `<auth_type>` can only be `plaintext` at this time, and the `<auth_value>`
|
||||||
|
is the plain text password itself.
|
||||||
|
|
||||||
|
The `<groups_array>` contains at least two groups. The first is a unique group
|
||||||
|
identifying that user and it's name is of the format `<user>:<account>`. The
|
||||||
|
second group is the `<account>` itself. Additional groups of `.admin` for
|
||||||
|
account administrators and `.reseller_admin` for reseller administrators may
|
||||||
|
exist. Here's an example user JSON dictionary::
|
||||||
|
|
||||||
|
{"auth": "plaintext:testing",
|
||||||
|
"groups": ["name": "test:tester", "name": "test", "name": ".admin"]}
|
||||||
|
|
||||||
|
To map an auth service account to a Swift storage account, the Service Account
|
||||||
|
Id string is stored in the `X-Container-Meta-Account-Id` header for the
|
||||||
|
<auth_account>/<account> container. To map back the other way, an
|
||||||
|
<auth_account>/.account_id/<account_id> object is created with the contents of
|
||||||
|
the corresponding auth service's account name.
|
||||||
|
|
||||||
|
Also, to support a future where the auth service will support multiple Swift
|
||||||
|
clusters or even multiple services for the same auth service account, an
|
||||||
|
<auth_account>/<account>/.services object is created with its contents having a
|
||||||
|
JSON dictionary of the format::
|
||||||
|
|
||||||
|
{"storage": {"default": "local", "local": <url>}}
|
||||||
|
|
||||||
|
The "default" is always "local" right now, and "local" is always the single
|
||||||
|
Swift cluster URL; but in the future there can be more than one cluster with
|
||||||
|
various names instead of just "local", and the "default" key's value will
|
||||||
|
contain the primary cluster to use for that account. Also, there may be more
|
||||||
|
services in addition to the current "storage" service right now.
|
||||||
|
|
||||||
|
Here's an example .services dictionary at the moment::
|
||||||
|
|
||||||
|
{"storage":
|
||||||
|
{"default": "local",
|
||||||
|
"local": "http://127.0.0.1:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"}}
|
||||||
|
|
||||||
|
But, here's an example of what the dictionary may look like in the future::
|
||||||
|
|
||||||
|
{"storage":
|
||||||
|
{"default": "dfw",
|
||||||
|
"dfw": "http://dfw.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||||
|
"ord": "http://ord.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||||
|
"sat": "http://ord.storage.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"},
|
||||||
|
"servers":
|
||||||
|
{"default": "dfw",
|
||||||
|
"dfw": "http://dfw.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||||
|
"ord": "http://ord.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||||
|
"sat": "http://ord.servers.com:8080/v1/AUTH_8980f74b1cda41e483cbe0a925f448a9"}}
|
||||||
|
|
||||||
|
Lastly, the tokens themselves are stored as objects in the
|
||||||
|
`<auth_account>/.token_[0-f]` containers. The names of the objects are the
|
||||||
|
token strings themselves, such as `AUTH_tked86bbd01864458aa2bd746879438d5a`.
|
||||||
|
The exact `.token_[0-f]` container chosen is based on the final digit of the
|
||||||
|
token name, such as `.token_a` for the token
|
||||||
|
`AUTH_tked86bbd01864458aa2bd746879438d5a`. The contents of the token objects
|
||||||
|
are JSON dictionaries of the format::
|
||||||
|
|
||||||
|
{"account": <account>,
|
||||||
|
"user": <user>,
|
||||||
|
"account_id": <account_id>,
|
||||||
|
"groups": <groups_array>,
|
||||||
|
"expires": <time.time() value>}
|
||||||
|
|
||||||
|
The `<account>` is the auth service account's name for that token. The `<user>`
|
||||||
|
is the user within the account for that token. The `<account_id>` is the
|
||||||
|
same as the `X-Container-Meta-Account-Id` for the auth service's account,
|
||||||
|
as described above. The `<groups_array>` is the user's groups, as described
|
||||||
|
above with the user object. The "expires" value indicates when the token is no
|
||||||
|
longer valid, as compared to Python's time.time() value.
|
||||||
|
|
||||||
|
Here's an example token object's JSON dictionary::
|
||||||
|
|
||||||
|
{"account": "test",
|
||||||
|
"user": "tester",
|
||||||
|
"account_id": "AUTH_8980f74b1cda41e483cbe0a925f448a9",
|
||||||
|
"groups": ["name": "test:tester", "name": "test", "name": ".admin"],
|
||||||
|
"expires": 1291273147.1624689}
|
||||||
|
|
||||||
|
To easily map a user to an already issued token, the token name is stored in
|
||||||
|
the user object's `X-Object-Meta-Auth-Token` header.
|
||||||
|
|
||||||
|
Here is an example full listing of an <auth_account>::
|
||||||
|
|
||||||
|
.account_id
|
||||||
|
AUTH_2282f516-559f-4966-b239-b5c88829e927
|
||||||
|
AUTH_f6f57a3c-33b5-4e85-95a5-a801e67505c8
|
||||||
|
AUTH_fea96a36-c177-4ca4-8c7e-b8c715d9d37b
|
||||||
|
.token_0
|
||||||
|
.token_1
|
||||||
|
.token_2
|
||||||
|
.token_3
|
||||||
|
.token_4
|
||||||
|
.token_5
|
||||||
|
.token_6
|
||||||
|
AUTH_tk9d2941b13d524b268367116ef956dee6
|
||||||
|
.token_7
|
||||||
|
.token_8
|
||||||
|
AUTH_tk93627c6324c64f78be746f1e6a4e3f98
|
||||||
|
.token_9
|
||||||
|
.token_a
|
||||||
|
.token_b
|
||||||
|
.token_c
|
||||||
|
.token_d
|
||||||
|
.token_e
|
||||||
|
AUTH_tk0d37d286af2c43ffad06e99112b3ec4e
|
||||||
|
.token_f
|
||||||
|
AUTH_tk766bbde93771489982d8dc76979d11cf
|
||||||
|
reseller
|
||||||
|
.services
|
||||||
|
reseller
|
||||||
|
test
|
||||||
|
.services
|
||||||
|
tester
|
||||||
|
tester3
|
||||||
|
test2
|
||||||
|
.services
|
||||||
|
tester2
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# Only needed for DevAuth; Swauth is within the proxy-server.conf
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
# bind_ip = 0.0.0.0
|
# bind_ip = 0.0.0.0
|
||||||
# bind_port = 11000
|
# bind_port = 11000
|
||||||
|
@ -55,5 +55,5 @@ use = egg:swift#object
|
|||||||
|
|
||||||
[object-auditor]
|
[object-auditor]
|
||||||
# log_name = object-auditor
|
# log_name = object-auditor
|
||||||
# Will audit, at most, 1 object per device per interval
|
# files_per_second = 20
|
||||||
# interval = 1800
|
# bytes_per_second = 10000000
|
||||||
|
@ -9,7 +9,10 @@
|
|||||||
# key_file = /etc/swift/proxy.key
|
# key_file = /etc/swift/proxy.key
|
||||||
|
|
||||||
[pipeline:main]
|
[pipeline:main]
|
||||||
|
# For DevAuth:
|
||||||
pipeline = catch_errors healthcheck cache ratelimit auth proxy-server
|
pipeline = catch_errors healthcheck cache ratelimit auth proxy-server
|
||||||
|
# For Swauth:
|
||||||
|
# pipeline = catch_errors healthcheck cache ratelimit swauth proxy-server
|
||||||
|
|
||||||
[app:proxy-server]
|
[app:proxy-server]
|
||||||
use = egg:swift#proxy
|
use = egg:swift#proxy
|
||||||
@ -33,6 +36,7 @@ use = egg:swift#proxy
|
|||||||
# 'false' no one, even authorized, can.
|
# 'false' no one, even authorized, can.
|
||||||
# allow_account_management = false
|
# allow_account_management = false
|
||||||
|
|
||||||
|
# Only needed for DevAuth
|
||||||
[filter:auth]
|
[filter:auth]
|
||||||
use = egg:swift#auth
|
use = egg:swift#auth
|
||||||
# The reseller prefix will verify a token begins with this prefix before even
|
# The reseller prefix will verify a token begins with this prefix before even
|
||||||
@ -44,8 +48,38 @@ use = egg:swift#auth
|
|||||||
# ip = 127.0.0.1
|
# ip = 127.0.0.1
|
||||||
# port = 11000
|
# port = 11000
|
||||||
# ssl = false
|
# ssl = false
|
||||||
|
# prefix = /
|
||||||
# node_timeout = 10
|
# node_timeout = 10
|
||||||
|
|
||||||
|
# Only needed for Swauth
|
||||||
|
[filter:swauth]
|
||||||
|
use = egg:swift#swauth
|
||||||
|
# log_name = auth-server
|
||||||
|
# log_facility = LOG_LOCAL0
|
||||||
|
# log_level = INFO
|
||||||
|
# log_headers = False
|
||||||
|
# The reseller prefix will verify a token begins with this prefix before even
|
||||||
|
# attempting to validate it. Also, with authorization, only Swift storage
|
||||||
|
# accounts with this prefix will be authorized by this middleware. Useful if
|
||||||
|
# multiple auth systems are in use for one Swift cluster.
|
||||||
|
# reseller_prefix = AUTH
|
||||||
|
# The auth prefix will cause requests beginning with this prefix to be routed
|
||||||
|
# to the auth subsystem, for granting tokens, creating accounts, users, etc.
|
||||||
|
# auth_prefix = /auth/
|
||||||
|
# Cluster strings are of the format name:url where name is a short name for the
|
||||||
|
# Swift cluster and url is the url to the proxy server(s) for the cluster.
|
||||||
|
# default_swift_cluster = local:http://127.0.0.1:8080/v1
|
||||||
|
# You may also use the format name::url::url where the first url is the one
|
||||||
|
# given to users to access their account (public url) and the second is the one
|
||||||
|
# used by swauth itself to create and delete accounts (private url). This is
|
||||||
|
# useful when a load balancer url should be used by users, but swauth itself is
|
||||||
|
# behind the load balancer. Example:
|
||||||
|
# default_swift_cluster = local::https://public.com:8080/v1::http://private.com:8080/v1
|
||||||
|
# token_life = 86400
|
||||||
|
# node_timeout = 10
|
||||||
|
# Highly recommended to change this.
|
||||||
|
super_admin_key = swauthkey
|
||||||
|
|
||||||
[filter:healthcheck]
|
[filter:healthcheck]
|
||||||
use = egg:swift#healthcheck
|
use = egg:swift#healthcheck
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
[stats]
|
[stats]
|
||||||
|
# For DevAuth:
|
||||||
auth_url = http://saio:11000/auth
|
auth_url = http://saio:11000/auth
|
||||||
|
# For Swauth:
|
||||||
|
# auth_url = http://saio:8080/auth/v1.0
|
||||||
auth_user = test:tester
|
auth_user = test:tester
|
||||||
auth_key = testing
|
auth_key = testing
|
||||||
# swift_dir = /etc/swift
|
# swift_dir = /etc/swift
|
||||||
|
8
setup.py
8
setup.py
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -21,6 +21,7 @@ import subprocess
|
|||||||
|
|
||||||
from swift import __version__ as version
|
from swift import __version__ as version
|
||||||
|
|
||||||
|
|
||||||
class local_sdist(sdist):
|
class local_sdist(sdist):
|
||||||
"""Customized sdist hook - builds the ChangeLog file from VC first"""
|
"""Customized sdist hook - builds the ChangeLog file from VC first"""
|
||||||
|
|
||||||
@ -79,6 +80,10 @@ setup(
|
|||||||
'bin/swift-log-uploader',
|
'bin/swift-log-uploader',
|
||||||
'bin/swift-log-stats-collector',
|
'bin/swift-log-stats-collector',
|
||||||
'bin/swift-account-stats-logger',
|
'bin/swift-account-stats-logger',
|
||||||
|
'bin/swauth-add-account', 'bin/swauth-add-user',
|
||||||
|
'bin/swauth-cleanup-tokens', 'bin/swauth-delete-account',
|
||||||
|
'bin/swauth-delete-user', 'bin/swauth-list', 'bin/swauth-prep',
|
||||||
|
'bin/swauth-set-account-service', 'bin/swift-auth-to-swauth',
|
||||||
],
|
],
|
||||||
entry_points={
|
entry_points={
|
||||||
'paste.app_factory': [
|
'paste.app_factory': [
|
||||||
@ -90,6 +95,7 @@ setup(
|
|||||||
],
|
],
|
||||||
'paste.filter_factory': [
|
'paste.filter_factory': [
|
||||||
'auth=swift.common.middleware.auth:filter_factory',
|
'auth=swift.common.middleware.auth:filter_factory',
|
||||||
|
'swauth=swift.common.middleware.swauth:filter_factory',
|
||||||
'healthcheck=swift.common.middleware.healthcheck:filter_factory',
|
'healthcheck=swift.common.middleware.healthcheck:filter_factory',
|
||||||
'memcache=swift.common.middleware.memcache:filter_factory',
|
'memcache=swift.common.middleware.memcache:filter_factory',
|
||||||
'ratelimit=swift.common.middleware.ratelimit:filter_factory',
|
'ratelimit=swift.common.middleware.ratelimit:filter_factory',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -35,6 +35,7 @@ class DevAuth(object):
|
|||||||
self.auth_host = conf.get('ip', '127.0.0.1')
|
self.auth_host = conf.get('ip', '127.0.0.1')
|
||||||
self.auth_port = int(conf.get('port', 11000))
|
self.auth_port = int(conf.get('port', 11000))
|
||||||
self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALUES
|
self.ssl = conf.get('ssl', 'false').lower() in TRUE_VALUES
|
||||||
|
self.auth_prefix = conf.get('prefix', '/')
|
||||||
self.timeout = int(conf.get('node_timeout', 10))
|
self.timeout = int(conf.get('node_timeout', 10))
|
||||||
|
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
@ -139,7 +140,9 @@ class DevAuth(object):
|
|||||||
if not groups:
|
if not groups:
|
||||||
with Timeout(self.timeout):
|
with Timeout(self.timeout):
|
||||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
||||||
'/token/%s' % token, headers, ssl=self.ssl)
|
'%stoken/%s' % (self.auth_prefix, token),
|
||||||
|
headers, ssl=self.ssl)
|
||||||
|
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
resp.read()
|
resp.read()
|
||||||
conn.close()
|
conn.close()
|
||||||
@ -166,9 +169,10 @@ class DevAuth(object):
|
|||||||
user_groups = (req.remote_user or '').split(',')
|
user_groups = (req.remote_user or '').split(',')
|
||||||
if '.reseller_admin' in user_groups:
|
if '.reseller_admin' in user_groups:
|
||||||
return None
|
return None
|
||||||
if account in user_groups and (req.method != 'PUT' or container):
|
if account in user_groups and \
|
||||||
|
(req.method not in ('DELETE', 'PUT') or container):
|
||||||
# If the user is admin for the account and is not trying to do an
|
# If the user is admin for the account and is not trying to do an
|
||||||
# account PUT...
|
# account DELETE or PUT...
|
||||||
return None
|
return None
|
||||||
referrers, groups = parse_acl(getattr(req, 'acl', None))
|
referrers, groups = parse_acl(getattr(req, 'acl', None))
|
||||||
if referrer_allowed(req.referer, referrers):
|
if referrer_allowed(req.referer, referrers):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -26,7 +26,11 @@ class CatchErrorMiddleware(object):
|
|||||||
|
|
||||||
def __init__(self, app, conf):
|
def __init__(self, app, conf):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.logger = get_logger(conf)
|
# if the application already has a logger we should use that one
|
||||||
|
self.logger = getattr(app, 'logger', None)
|
||||||
|
if not self.logger:
|
||||||
|
# and only call get_logger if we have to
|
||||||
|
self.logger = get_logger(conf)
|
||||||
|
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
try:
|
try:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
1312
swift/common/middleware/swauth.py
Normal file
1312
swift/common/middleware/swauth.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -14,7 +14,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from array import array
|
from array import array
|
||||||
from random import randint
|
from random import randint, shuffle
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from swift.common.ring import RingData
|
from swift.common.ring import RingData
|
||||||
@ -413,6 +413,7 @@ class RingBuilder(object):
|
|||||||
dev['parts_wanted'] += 1
|
dev['parts_wanted'] += 1
|
||||||
dev['parts'] -= 1
|
dev['parts'] -= 1
|
||||||
reassign_parts.append(part)
|
reassign_parts.append(part)
|
||||||
|
shuffle(reassign_parts)
|
||||||
return reassign_parts
|
return reassign_parts
|
||||||
|
|
||||||
def _reassign_parts(self, reassign_parts):
|
def _reassign_parts(self, reassign_parts):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -284,11 +284,15 @@ class LoggerFileObject(object):
|
|||||||
|
|
||||||
|
|
||||||
class LogAdapter(object):
|
class LogAdapter(object):
|
||||||
"""Cheesy version of the LoggerAdapter available in Python 3"""
|
"""
|
||||||
|
A Logger like object which performs some reformatting on calls to
|
||||||
|
:meth:`exception`. Can be used to store a threadlocal transaction id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_txn_id = threading.local()
|
||||||
|
|
||||||
def __init__(self, logger):
|
def __init__(self, logger):
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self._txn_id = threading.local()
|
|
||||||
for proxied_method in ('debug', 'log', 'warn', 'warning', 'error',
|
for proxied_method in ('debug', 'log', 'warn', 'warning', 'error',
|
||||||
'critical', 'info'):
|
'critical', 'info'):
|
||||||
setattr(self, proxied_method, getattr(logger, proxied_method))
|
setattr(self, proxied_method, getattr(logger, proxied_method))
|
||||||
@ -306,7 +310,7 @@ class LogAdapter(object):
|
|||||||
return self.logger.getEffectiveLevel()
|
return self.logger.getEffectiveLevel()
|
||||||
|
|
||||||
def exception(self, msg, *args):
|
def exception(self, msg, *args):
|
||||||
_, exc, _ = sys.exc_info()
|
_junk, exc, _junk = sys.exc_info()
|
||||||
call = self.logger.error
|
call = self.logger.error
|
||||||
emsg = ''
|
emsg = ''
|
||||||
if isinstance(exc, OSError):
|
if isinstance(exc, OSError):
|
||||||
@ -316,9 +320,11 @@ class LogAdapter(object):
|
|||||||
call = self.logger.exception
|
call = self.logger.exception
|
||||||
elif isinstance(exc, socket.error):
|
elif isinstance(exc, socket.error):
|
||||||
if exc.errno == errno.ECONNREFUSED:
|
if exc.errno == errno.ECONNREFUSED:
|
||||||
emsg = 'Connection refused'
|
emsg = _('Connection refused')
|
||||||
elif exc.errno == errno.EHOSTUNREACH:
|
elif exc.errno == errno.EHOSTUNREACH:
|
||||||
emsg = 'Host unreachable'
|
emsg = _('Host unreachable')
|
||||||
|
elif exc.errno == errno.ETIMEDOUT:
|
||||||
|
emsg = _('Connection timeout')
|
||||||
else:
|
else:
|
||||||
call = self.logger.exception
|
call = self.logger.exception
|
||||||
elif isinstance(exc, eventlet.Timeout):
|
elif isinstance(exc, eventlet.Timeout):
|
||||||
@ -334,18 +340,45 @@ class LogAdapter(object):
|
|||||||
|
|
||||||
|
|
||||||
class NamedFormatter(logging.Formatter):
|
class NamedFormatter(logging.Formatter):
|
||||||
def __init__(self, server, logger):
|
"""
|
||||||
logging.Formatter.__init__(self)
|
NamedFormatter is used to add additional information to log messages.
|
||||||
|
Normally it will simply add the server name as an attribute on the
|
||||||
|
LogRecord and the default format string will include it at the
|
||||||
|
begining of the log message. Additionally, if the transaction id is
|
||||||
|
available and not already included in the message, NamedFormatter will
|
||||||
|
add it.
|
||||||
|
|
||||||
|
NamedFormatter may be initialized with a format string which makes use
|
||||||
|
of the standard LogRecord attributes. In addition the format string
|
||||||
|
may include the following mapping key:
|
||||||
|
|
||||||
|
+----------------+---------------------------------------------+
|
||||||
|
| Format | Description |
|
||||||
|
+================+=============================================+
|
||||||
|
| %(server)s | Name of the swift server doing logging |
|
||||||
|
+----------------+---------------------------------------------+
|
||||||
|
|
||||||
|
:param server: the swift server name, a string.
|
||||||
|
:param logger: a Logger or :class:`LogAdapter` instance, additional
|
||||||
|
context may be pulled from attributes on this logger if
|
||||||
|
available.
|
||||||
|
:param fmt: the format string used to construct the message, if none is
|
||||||
|
supplied it defaults to ``"%(server)s %(message)s"``
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, server, logger,
|
||||||
|
fmt="%(server)s %(message)s"):
|
||||||
|
logging.Formatter.__init__(self, fmt)
|
||||||
self.server = server
|
self.server = server
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
|
record.server = self.server
|
||||||
msg = logging.Formatter.format(self, record)
|
msg = logging.Formatter.format(self, record)
|
||||||
if self.logger.txn_id and (record.levelno != logging.INFO or
|
if self.logger.txn_id and (record.levelno != logging.INFO or
|
||||||
self.logger.txn_id not in msg):
|
self.logger.txn_id not in msg):
|
||||||
return '%s %s (txn: %s)' % (self.server, msg, self.logger.txn_id)
|
msg = "%s (txn: %s)" % (msg, self.logger.txn_id)
|
||||||
else:
|
return msg
|
||||||
return '%s %s' % (self.server, msg)
|
|
||||||
|
|
||||||
|
|
||||||
def get_logger(conf, name=None, log_to_console=False):
|
def get_logger(conf, name=None, log_to_console=False):
|
||||||
@ -365,6 +398,7 @@ def get_logger(conf, name=None, log_to_console=False):
|
|||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
if hasattr(get_logger, 'handler') and get_logger.handler:
|
if hasattr(get_logger, 'handler') and get_logger.handler:
|
||||||
root_logger.removeHandler(get_logger.handler)
|
root_logger.removeHandler(get_logger.handler)
|
||||||
|
get_logger.handler.close()
|
||||||
get_logger.handler = None
|
get_logger.handler = None
|
||||||
if log_to_console:
|
if log_to_console:
|
||||||
# check if a previous call to get_logger already added a console logger
|
# check if a previous call to get_logger already added a console logger
|
||||||
@ -386,7 +420,10 @@ def get_logger(conf, name=None, log_to_console=False):
|
|||||||
root_logger.setLevel(
|
root_logger.setLevel(
|
||||||
getattr(logging, conf.get('log_level', 'INFO').upper(), logging.INFO))
|
getattr(logging, conf.get('log_level', 'INFO').upper(), logging.INFO))
|
||||||
adapted_logger = LogAdapter(root_logger)
|
adapted_logger = LogAdapter(root_logger)
|
||||||
get_logger.handler.setFormatter(NamedFormatter(name, adapted_logger))
|
formatter = NamedFormatter(name, adapted_logger)
|
||||||
|
get_logger.handler.setFormatter(formatter)
|
||||||
|
if hasattr(get_logger, 'console'):
|
||||||
|
get_logger.console.setFormatter(formatter)
|
||||||
return adapted_logger
|
return adapted_logger
|
||||||
|
|
||||||
|
|
||||||
@ -744,19 +781,22 @@ def audit_location_generator(devices, datadir, mount_check=True, logger=None):
|
|||||||
on devices
|
on devices
|
||||||
:param logger: a logger object
|
:param logger: a logger object
|
||||||
'''
|
'''
|
||||||
for device in os.listdir(devices):
|
device_dir = os.listdir(devices)
|
||||||
if mount_check and not\
|
# randomize devices in case of process restart before sweep completed
|
||||||
|
shuffle(device_dir)
|
||||||
|
for device in device_dir:
|
||||||
|
if mount_check and not \
|
||||||
os.path.ismount(os.path.join(devices, device)):
|
os.path.ismount(os.path.join(devices, device)):
|
||||||
if logger:
|
if logger:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
_('Skipping %s as it is not mounted'), device)
|
_('Skipping %s as it is not mounted'), device)
|
||||||
continue
|
continue
|
||||||
datadir = os.path.join(devices, device, datadir)
|
datadir_path = os.path.join(devices, device, datadir)
|
||||||
if not os.path.exists(datadir):
|
if not os.path.exists(datadir_path):
|
||||||
continue
|
continue
|
||||||
partitions = os.listdir(datadir)
|
partitions = os.listdir(datadir_path)
|
||||||
for partition in partitions:
|
for partition in partitions:
|
||||||
part_path = os.path.join(datadir, partition)
|
part_path = os.path.join(datadir_path, partition)
|
||||||
if not os.path.isdir(part_path):
|
if not os.path.isdir(part_path):
|
||||||
continue
|
continue
|
||||||
suffixes = os.listdir(part_path)
|
suffixes = os.listdir(part_path)
|
||||||
@ -773,3 +813,30 @@ def audit_location_generator(devices, datadir, mount_check=True, logger=None):
|
|||||||
reverse=True):
|
reverse=True):
|
||||||
path = os.path.join(hash_path, fname)
|
path = os.path.join(hash_path, fname)
|
||||||
yield path, device, partition
|
yield path, device, partition
|
||||||
|
|
||||||
|
|
||||||
|
def ratelimit_sleep(running_time, max_rate, incr_by=1):
|
||||||
|
'''
|
||||||
|
Will eventlet.sleep() for the appropriate time so that the max_rate
|
||||||
|
is never exceeded. If max_rate is 0, will not ratelimit. The
|
||||||
|
maximum recommended rate should not exceed (1000 * incr_by) a second
|
||||||
|
as eventlet.sleep() does involve some overhead. Returns running_time
|
||||||
|
that should be used for subsequent calls.
|
||||||
|
|
||||||
|
:param running_time: the running time of the next allowable request. Best
|
||||||
|
to start at zero.
|
||||||
|
:param max_rate: The maximum rate per second allowed for the process.
|
||||||
|
:param incr_by: How much to increment the counter. Useful if you want
|
||||||
|
to ratelimit 1024 bytes/sec and have differing sizes
|
||||||
|
of requests. Must be >= 0.
|
||||||
|
'''
|
||||||
|
if not max_rate or incr_by <= 0:
|
||||||
|
return running_time
|
||||||
|
clock_accuracy = 1000.0
|
||||||
|
now = time.time() * clock_accuracy
|
||||||
|
time_per_request = clock_accuracy * (float(incr_by) / max_rate)
|
||||||
|
if running_time < now:
|
||||||
|
running_time = now
|
||||||
|
elif running_time - now > time_per_request:
|
||||||
|
eventlet.sleep((running_time - now) / clock_accuracy)
|
||||||
|
return running_time + time_per_request
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -20,7 +20,8 @@ from random import random
|
|||||||
|
|
||||||
from swift.obj import server as object_server
|
from swift.obj import server as object_server
|
||||||
from swift.obj.replicator import invalidate_hash
|
from swift.obj.replicator import invalidate_hash
|
||||||
from swift.common.utils import get_logger, renamer, audit_location_generator
|
from swift.common.utils import get_logger, renamer, audit_location_generator, \
|
||||||
|
ratelimit_sleep
|
||||||
from swift.common.exceptions import AuditException
|
from swift.common.exceptions import AuditException
|
||||||
from swift.common.daemon import Daemon
|
from swift.common.daemon import Daemon
|
||||||
|
|
||||||
@ -34,39 +35,30 @@ class ObjectAuditor(Daemon):
|
|||||||
self.devices = conf.get('devices', '/srv/node')
|
self.devices = conf.get('devices', '/srv/node')
|
||||||
self.mount_check = conf.get('mount_check', 'true').lower() in \
|
self.mount_check = conf.get('mount_check', 'true').lower() in \
|
||||||
('true', 't', '1', 'on', 'yes', 'y')
|
('true', 't', '1', 'on', 'yes', 'y')
|
||||||
self.interval = int(conf.get('interval', 1800))
|
self.max_files_per_second = float(conf.get('files_per_second', 20))
|
||||||
|
self.max_bytes_per_second = float(conf.get('bytes_per_second',
|
||||||
|
10000000))
|
||||||
|
self.files_running_time = 0
|
||||||
|
self.bytes_running_time = 0
|
||||||
|
self.bytes_processed = 0
|
||||||
|
self.total_bytes_processed = 0
|
||||||
|
self.total_files_processed = 0
|
||||||
self.passes = 0
|
self.passes = 0
|
||||||
self.quarantines = 0
|
self.quarantines = 0
|
||||||
self.errors = 0
|
self.errors = 0
|
||||||
|
self.log_time = 3600 # once an hour
|
||||||
|
|
||||||
def run_forever(self): # pragma: no cover
|
def run_forever(self):
|
||||||
"""Run the object audit until stopped."""
|
"""Run the object audit until stopped."""
|
||||||
reported = time.time()
|
|
||||||
time.sleep(random() * self.interval)
|
|
||||||
while True:
|
while True:
|
||||||
begin = time.time()
|
self.run_once('forever')
|
||||||
all_locs = audit_location_generator(self.devices,
|
self.total_bytes_processed = 0
|
||||||
object_server.DATADIR,
|
self.total_files_processed = 0
|
||||||
mount_check=self.mount_check,
|
time.sleep(30)
|
||||||
logger=self.logger)
|
|
||||||
for path, device, partition in all_locs:
|
|
||||||
self.object_audit(path, device, partition)
|
|
||||||
if time.time() - reported >= 3600: # once an hour
|
|
||||||
self.logger.info(_('Since %(time)s: Locally: %(pass)d '
|
|
||||||
'passed audit, %(quar)d quarantined, %(error)d errors'),
|
|
||||||
{'time': time.ctime(reported), 'pass': self.passes,
|
|
||||||
'quar': self.quarantines, 'error': self.errors})
|
|
||||||
reported = time.time()
|
|
||||||
self.passes = 0
|
|
||||||
self.quarantines = 0
|
|
||||||
self.errors = 0
|
|
||||||
elapsed = time.time() - begin
|
|
||||||
if elapsed < self.interval:
|
|
||||||
time.sleep(self.interval - elapsed)
|
|
||||||
|
|
||||||
def run_once(self):
|
def run_once(self, mode='once'):
|
||||||
"""Run the object audit once."""
|
"""Run the object audit once."""
|
||||||
self.logger.info(_('Begin object audit "once" mode'))
|
self.logger.info(_('Begin object audit "%s" mode' % mode))
|
||||||
begin = reported = time.time()
|
begin = reported = time.time()
|
||||||
all_locs = audit_location_generator(self.devices,
|
all_locs = audit_location_generator(self.devices,
|
||||||
object_server.DATADIR,
|
object_server.DATADIR,
|
||||||
@ -74,18 +66,35 @@ class ObjectAuditor(Daemon):
|
|||||||
logger=self.logger)
|
logger=self.logger)
|
||||||
for path, device, partition in all_locs:
|
for path, device, partition in all_locs:
|
||||||
self.object_audit(path, device, partition)
|
self.object_audit(path, device, partition)
|
||||||
if time.time() - reported >= 3600: # once an hour
|
self.files_running_time = ratelimit_sleep(
|
||||||
self.logger.info(_('Since %(time)s: Locally: %(pass)d '
|
self.files_running_time, self.max_files_per_second)
|
||||||
'passed audit, %(quar)d quarantined, %(error)d errors'),
|
self.total_files_processed += 1
|
||||||
{'time': time.ctime(reported), 'pass': self.passes,
|
if time.time() - reported >= self.log_time:
|
||||||
'quar': self.quarantines, 'error': self.errors})
|
self.logger.info(_(
|
||||||
|
'Since %(start_time)s: Locally: %(passes)d passed audit, '
|
||||||
|
'%(quars)d quarantined, %(errors)d errors '
|
||||||
|
'files/sec: %(frate).2f , bytes/sec: %(brate).2f') % {
|
||||||
|
'start_time': time.ctime(reported),
|
||||||
|
'passes': self.passes,
|
||||||
|
'quars': self.quarantines,
|
||||||
|
'errors': self.errors,
|
||||||
|
'frate': self.passes / (time.time() - reported),
|
||||||
|
'brate': self.bytes_processed /
|
||||||
|
(time.time() - reported)})
|
||||||
reported = time.time()
|
reported = time.time()
|
||||||
self.passes = 0
|
self.passes = 0
|
||||||
self.quarantines = 0
|
self.quarantines = 0
|
||||||
self.errors = 0
|
self.errors = 0
|
||||||
|
self.bytes_processed = 0
|
||||||
elapsed = time.time() - begin
|
elapsed = time.time() - begin
|
||||||
self.logger.info(
|
self.logger.info(_(
|
||||||
_('Object audit "once" mode completed: %.02fs'), elapsed)
|
'Object audit "%(mode)s" mode completed: %(elapsed).02fs. '
|
||||||
|
'Total files/sec: %(frate).2f , '
|
||||||
|
'Total bytes/sec: %(brate).2f ') % {
|
||||||
|
'mode': mode,
|
||||||
|
'elapsed': elapsed,
|
||||||
|
'frate': self.total_files_processed / elapsed,
|
||||||
|
'brate': self.total_bytes_processed / elapsed})
|
||||||
|
|
||||||
def object_audit(self, path, device, partition):
|
def object_audit(self, path, device, partition):
|
||||||
"""
|
"""
|
||||||
@ -102,7 +111,7 @@ class ObjectAuditor(Daemon):
|
|||||||
name = object_server.read_metadata(path)['name']
|
name = object_server.read_metadata(path)['name']
|
||||||
except Exception, exc:
|
except Exception, exc:
|
||||||
raise AuditException('Error when reading metadata: %s' % exc)
|
raise AuditException('Error when reading metadata: %s' % exc)
|
||||||
_, account, container, obj = name.split('/', 3)
|
_junk, account, container, obj = name.split('/', 3)
|
||||||
df = object_server.DiskFile(self.devices, device,
|
df = object_server.DiskFile(self.devices, device,
|
||||||
partition, account,
|
partition, account,
|
||||||
container, obj,
|
container, obj,
|
||||||
@ -117,15 +126,20 @@ class ObjectAuditor(Daemon):
|
|||||||
os.path.getsize(df.data_file)))
|
os.path.getsize(df.data_file)))
|
||||||
etag = md5()
|
etag = md5()
|
||||||
for chunk in df:
|
for chunk in df:
|
||||||
|
self.bytes_running_time = ratelimit_sleep(
|
||||||
|
self.bytes_running_time, self.max_bytes_per_second,
|
||||||
|
incr_by=len(chunk))
|
||||||
etag.update(chunk)
|
etag.update(chunk)
|
||||||
|
self.bytes_processed += len(chunk)
|
||||||
|
self.total_bytes_processed += len(chunk)
|
||||||
etag = etag.hexdigest()
|
etag = etag.hexdigest()
|
||||||
if etag != df.metadata['ETag']:
|
if etag != df.metadata['ETag']:
|
||||||
raise AuditException("ETag of %s does not match file's md5 of "
|
raise AuditException("ETag of %s does not match file's md5 of "
|
||||||
"%s" % (df.metadata['ETag'], etag))
|
"%s" % (df.metadata['ETag'], etag))
|
||||||
except AuditException, err:
|
except AuditException, err:
|
||||||
self.quarantines += 1
|
self.quarantines += 1
|
||||||
self.logger.error(_('ERROR Object %(obj)s failed audit and will be '
|
self.logger.error(_('ERROR Object %(obj)s failed audit and will '
|
||||||
'quarantined: %(err)s'), {'obj': path, 'err': err})
|
'be quarantined: %(err)s'), {'obj': path, 'err': err})
|
||||||
invalidate_hash(os.path.dirname(path))
|
invalidate_hash(os.path.dirname(path))
|
||||||
renamer_path = os.path.dirname(path)
|
renamer_path = os.path.dirname(path)
|
||||||
renamer(renamer_path, os.path.join(self.devices, device,
|
renamer(renamer_path, os.path.join(self.devices, device,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -179,7 +179,7 @@ def get_hashes(partition_dir, do_listdir=True, reclaim_age=ONE_WEEK):
|
|||||||
hashes[suffix] = hash_suffix(suffix_dir, reclaim_age)
|
hashes[suffix] = hash_suffix(suffix_dir, reclaim_age)
|
||||||
hashed += 1
|
hashed += 1
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception('Error hashing suffix')
|
logging.exception(_('Error hashing suffix'))
|
||||||
hashes[suffix] = None
|
hashes[suffix] = None
|
||||||
else:
|
else:
|
||||||
del hashes[suffix]
|
del hashes[suffix]
|
||||||
@ -254,8 +254,8 @@ class ObjectReplicator(Daemon):
|
|||||||
continue
|
continue
|
||||||
self.logger.info(result)
|
self.logger.info(result)
|
||||||
if ret_val:
|
if ret_val:
|
||||||
self.logger.error(_('Bad rsync return code: %s -> %d'),
|
self.logger.error(_('Bad rsync return code: %(args)s -> %(ret)d'),
|
||||||
(str(args), ret_val))
|
{'args': str(args), 'ret': ret_val})
|
||||||
elif results:
|
elif results:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
_("Successful rsync of %(src)s at %(dst)s (%(time).03f)"),
|
_("Successful rsync of %(src)s at %(dst)s (%(time).03f)"),
|
||||||
@ -407,7 +407,7 @@ class ObjectReplicator(Daemon):
|
|||||||
conn.getresponse().read()
|
conn.getresponse().read()
|
||||||
self.suffix_sync += len(suffixes)
|
self.suffix_sync += len(suffixes)
|
||||||
except (Exception, Timeout):
|
except (Exception, Timeout):
|
||||||
logging.exception("Error syncing with node: %s" % node)
|
self.logger.exception(_("Error syncing with node: %s") % node)
|
||||||
self.suffix_count += len(local_hash)
|
self.suffix_count += len(local_hash)
|
||||||
except (Exception, Timeout):
|
except (Exception, Timeout):
|
||||||
self.logger.exception(_("Error syncing partition"))
|
self.logger.exception(_("Error syncing partition"))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -72,6 +72,21 @@ def read_metadata(fd):
|
|||||||
return pickle.loads(metadata)
|
return pickle.loads(metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def write_metadata(fd, metadata):
|
||||||
|
"""
|
||||||
|
Helper function to write pickled metadata for an object file.
|
||||||
|
|
||||||
|
:param fd: file descriptor to write the metadata
|
||||||
|
:param metadata: metadata to write
|
||||||
|
"""
|
||||||
|
metastr = pickle.dumps(metadata, PICKLE_PROTOCOL)
|
||||||
|
key = 0
|
||||||
|
while metastr:
|
||||||
|
setxattr(fd, '%s%s' % (METADATA_KEY, key or ''), metastr[:254])
|
||||||
|
metastr = metastr[254:]
|
||||||
|
key += 1
|
||||||
|
|
||||||
|
|
||||||
class DiskFile(object):
|
class DiskFile(object):
|
||||||
"""
|
"""
|
||||||
Manage object files on disk.
|
Manage object files on disk.
|
||||||
@ -97,6 +112,7 @@ class DiskFile(object):
|
|||||||
self.metadata = {}
|
self.metadata = {}
|
||||||
self.meta_file = None
|
self.meta_file = None
|
||||||
self.data_file = None
|
self.data_file = None
|
||||||
|
self.fp = None
|
||||||
if not os.path.exists(self.datadir):
|
if not os.path.exists(self.datadir):
|
||||||
return
|
return
|
||||||
files = sorted(os.listdir(self.datadir), reverse=True)
|
files = sorted(os.listdir(self.datadir), reverse=True)
|
||||||
@ -203,17 +219,12 @@ class DiskFile(object):
|
|||||||
|
|
||||||
:params fd: file descriptor of the temp file
|
:params fd: file descriptor of the temp file
|
||||||
:param tmppath: path to the temporary file being used
|
:param tmppath: path to the temporary file being used
|
||||||
:param metadata: dictionary of metada to be written
|
:param metadata: dictionary of metadata to be written
|
||||||
:param extention: extension to be used when making the file
|
:param extention: extension to be used when making the file
|
||||||
"""
|
"""
|
||||||
metadata['name'] = self.name
|
metadata['name'] = self.name
|
||||||
timestamp = normalize_timestamp(metadata['X-Timestamp'])
|
timestamp = normalize_timestamp(metadata['X-Timestamp'])
|
||||||
metastr = pickle.dumps(metadata, PICKLE_PROTOCOL)
|
write_metadata(fd, metadata)
|
||||||
key = 0
|
|
||||||
while metastr:
|
|
||||||
setxattr(fd, '%s%s' % (METADATA_KEY, key or ''), metastr[:254])
|
|
||||||
metastr = metastr[254:]
|
|
||||||
key += 1
|
|
||||||
if 'Content-Length' in metadata:
|
if 'Content-Length' in metadata:
|
||||||
drop_buffer_cache(fd, 0, int(metadata['Content-Length']))
|
drop_buffer_cache(fd, 0, int(metadata['Content-Length']))
|
||||||
os.fsync(fd)
|
os.fsync(fd)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -911,12 +911,14 @@ class ObjectController(Controller):
|
|||||||
self.account_name, self.container_name, self.object_name)
|
self.account_name, self.container_name, self.object_name)
|
||||||
req.headers['X-Timestamp'] = normalize_timestamp(time.time())
|
req.headers['X-Timestamp'] = normalize_timestamp(time.time())
|
||||||
# Sometimes the 'content-type' header exists, but is set to None.
|
# Sometimes the 'content-type' header exists, but is set to None.
|
||||||
|
content_type_manually_set = True
|
||||||
if not req.headers.get('content-type'):
|
if not req.headers.get('content-type'):
|
||||||
guessed_type, _junk = mimetypes.guess_type(req.path_info)
|
guessed_type, _junk = mimetypes.guess_type(req.path_info)
|
||||||
if not guessed_type:
|
if not guessed_type:
|
||||||
req.headers['Content-Type'] = 'application/octet-stream'
|
req.headers['Content-Type'] = 'application/octet-stream'
|
||||||
else:
|
else:
|
||||||
req.headers['Content-Type'] = guessed_type
|
req.headers['Content-Type'] = guessed_type
|
||||||
|
content_type_manually_set = False
|
||||||
error_response = check_object_creation(req, self.object_name)
|
error_response = check_object_creation(req, self.object_name)
|
||||||
if error_response:
|
if error_response:
|
||||||
return error_response
|
return error_response
|
||||||
@ -950,17 +952,20 @@ class ObjectController(Controller):
|
|||||||
self.container_name = orig_container_name
|
self.container_name = orig_container_name
|
||||||
new_req = Request.blank(req.path_info,
|
new_req = Request.blank(req.path_info,
|
||||||
environ=req.environ, headers=req.headers)
|
environ=req.environ, headers=req.headers)
|
||||||
if 'x-object-manifest' in source_resp.headers:
|
data_source = source_resp.app_iter
|
||||||
data_source = iter([''])
|
new_req.content_length = source_resp.content_length
|
||||||
new_req.content_length = 0
|
if new_req.content_length is None:
|
||||||
new_req.headers['X-Object-Manifest'] = \
|
# This indicates a transfer-encoding: chunked source object,
|
||||||
source_resp.headers['x-object-manifest']
|
# which currently only happens because there are more than
|
||||||
else:
|
# CONTAINER_LISTING_LIMIT segments in a segmented object. In
|
||||||
data_source = source_resp.app_iter
|
# this case, we're going to refuse to do the server-side copy.
|
||||||
new_req.content_length = source_resp.content_length
|
return HTTPRequestEntityTooLarge(request=req)
|
||||||
new_req.etag = source_resp.etag
|
new_req.etag = source_resp.etag
|
||||||
# we no longer need the X-Copy-From header
|
# we no longer need the X-Copy-From header
|
||||||
del new_req.headers['X-Copy-From']
|
del new_req.headers['X-Copy-From']
|
||||||
|
if not content_type_manually_set:
|
||||||
|
new_req.headers['Content-Type'] = \
|
||||||
|
source_resp.headers['Content-Type']
|
||||||
for k, v in source_resp.headers.items():
|
for k, v in source_resp.headers.items():
|
||||||
if k.lower().startswith('x-object-meta-'):
|
if k.lower().startswith('x-object-meta-'):
|
||||||
new_req.headers[k] = v
|
new_req.headers[k] = v
|
||||||
@ -1683,7 +1688,8 @@ class BaseApplication(object):
|
|||||||
def update_request(self, req):
|
def update_request(self, req):
|
||||||
req.bytes_transferred = '-'
|
req.bytes_transferred = '-'
|
||||||
req.client_disconnect = False
|
req.client_disconnect = False
|
||||||
req.headers['x-cf-trans-id'] = 'tx' + str(uuid.uuid4())
|
if 'x-cf-trans-id' not in req.headers:
|
||||||
|
req.headers['x-cf-trans-id'] = 'tx' + str(uuid.uuid4())
|
||||||
if 'x-storage-token' in req.headers and \
|
if 'x-storage-token' in req.headers and \
|
||||||
'x-auth-token' not in req.headers:
|
'x-auth-token' not in req.headers:
|
||||||
req.headers['x-auth-token'] = req.headers['x-storage-token']
|
req.headers['x-auth-token'] = req.headers['x-storage-token']
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
# sample config
|
# sample config
|
||||||
auth_host = 127.0.0.1
|
auth_host = 127.0.0.1
|
||||||
|
# For DevAuth:
|
||||||
auth_port = 11000
|
auth_port = 11000
|
||||||
|
# For Swauth:
|
||||||
|
# auth_port = 8080
|
||||||
auth_ssl = no
|
auth_ssl = no
|
||||||
|
# For Swauth:
|
||||||
|
# auth_prefix = /auth/
|
||||||
|
|
||||||
# Primary functional test account (needs admin access to the account)
|
# Primary functional test account (needs admin access to the account)
|
||||||
account = test
|
account = test
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2010 OpenStack, LLC.
|
# Copyright (c) 2010-2011 OpenStack, LLC.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -82,6 +82,7 @@ class Connection(object):
|
|||||||
self.auth_host = config['auth_host']
|
self.auth_host = config['auth_host']
|
||||||
self.auth_port = int(config['auth_port'])
|
self.auth_port = int(config['auth_port'])
|
||||||
self.auth_ssl = config['auth_ssl'] in ('on', 'true', 'yes', '1')
|
self.auth_ssl = config['auth_ssl'] in ('on', 'true', 'yes', '1')
|
||||||
|
self.auth_prefix = config.get('auth_prefix', '/')
|
||||||
|
|
||||||
self.account = config['account']
|
self.account = config['account']
|
||||||
self.username = config['username']
|
self.username = config['username']
|
||||||
@ -105,11 +106,11 @@ class Connection(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'x-storage-user': self.username,
|
'x-auth-user': '%s:%s' % (self.account, self.username),
|
||||||
'x-storage-pass': self.password,
|
'x-auth-key': self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
path = '/v1/%s/auth' % (self.account)
|
path = '%sv1.0' % (self.auth_prefix)
|
||||||
if self.auth_ssl:
|
if self.auth_ssl:
|
||||||
connection = httplib.HTTPSConnection(self.auth_host,
|
connection = httplib.HTTPSConnection(self.auth_host,
|
||||||
port=self.auth_port)
|
port=self.auth_port)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user