
The wrapper swift wrote for IPv6 support hasn't been necessary since dropping support for Python 2.6 back in 2015. See - https://github.com/openstack/swift/commit/67de0c8 - https://github.com/python/cpython/commit/8c6d9d7 - https://bugs.python.org/issue2987 Drive-by: use range() since xrange() doesn't exist on py3 and we're only going to 16; clean up an invalid escape sequence. Change-Id: Ib124cb27edd4c3defdb4a9e2404dcdcb71e6dd99
170 lines
7.6 KiB
Python
Executable File
170 lines
7.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright (c) 2010-2011 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.
|
|
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
from getpass import getpass
|
|
import gettext
|
|
import json
|
|
from optparse import OptionParser
|
|
import re
|
|
from sys import argv
|
|
from sys import exit
|
|
from time import sleep
|
|
from time import time
|
|
|
|
from swiftclient.client import ClientException
|
|
from swiftclient.client import Connection
|
|
|
|
if __name__ == '__main__':
|
|
gettext.install('swauth', 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.')
|
|
parser.add_option('', '--purge', dest='purge_account', help='Purges all '
|
|
'tokens for a given account whether the tokens have expired or not.')
|
|
parser.add_option('', '--purge-all', dest='purge_all', action='store_true',
|
|
default=False, help='Purges all tokens for all accounts and users '
|
|
'whether the tokens have expired or not.')
|
|
args = argv[1:]
|
|
if not args:
|
|
args.append('-h')
|
|
(options, args) = parser.parse_args(args)
|
|
if len(args) != 0:
|
|
parser.parse_args(['-h'])
|
|
if not options.admin_key:
|
|
options.admin_key = getpass()
|
|
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)
|
|
if options.purge_account:
|
|
marker = None
|
|
while True:
|
|
if options.verbose:
|
|
print('GET %s?marker=%s' % (options.purge_account, marker))
|
|
objs = conn.get_container(options.purge_account, marker=marker)[1]
|
|
if objs:
|
|
marker = objs[-1]['name']
|
|
else:
|
|
if options.verbose:
|
|
print('No more objects in %s' % options.purge_account)
|
|
break
|
|
for obj in objs:
|
|
if options.verbose:
|
|
print('HEAD %s/%s' % (options.purge_account, obj['name']))
|
|
headers = conn.head_object(options.purge_account, obj['name'])
|
|
if 'x-object-meta-auth-token' in headers:
|
|
token = headers['x-object-meta-auth-token']
|
|
container = '.token_%s' % token[-1]
|
|
if options.verbose:
|
|
print('%s/%s purge account %r; deleting' %
|
|
(container, token, options.purge_account))
|
|
print('DELETE %s/%s' % (container, token))
|
|
try:
|
|
conn.delete_object(container, token)
|
|
except ClientException as err:
|
|
if err.http_status != 404:
|
|
raise
|
|
continue
|
|
if options.verbose:
|
|
print('Done.')
|
|
exit(0)
|
|
for x in range(16):
|
|
container = '.token_%x' % x
|
|
marker = None
|
|
while True:
|
|
if options.verbose:
|
|
print('GET %s?marker=%s' % (container, marker))
|
|
try:
|
|
objs = conn.get_container(container, marker=marker)[1]
|
|
except ClientException as e:
|
|
if e.http_status == 404:
|
|
exit('Container %s not found. swauth-prep needs to be '
|
|
'rerun' % (container))
|
|
else:
|
|
exit('Object listing on container %s failed with status '
|
|
'code %d' % (container, e.http_status))
|
|
if objs:
|
|
marker = objs[-1]['name']
|
|
else:
|
|
if options.verbose:
|
|
print('No more objects in %s' % container)
|
|
break
|
|
for obj in objs:
|
|
if options.purge_all:
|
|
if options.verbose:
|
|
print('%s/%s purge all; deleting' %
|
|
(container, obj['name']))
|
|
print('DELETE %s/%s' % (container, obj['name']))
|
|
try:
|
|
conn.delete_object(container, obj['name'])
|
|
except ClientException as err:
|
|
if err.http_status != 404:
|
|
raise
|
|
continue
|
|
last_modified = datetime(*map(int, re.split(r'[^\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']))
|
|
try:
|
|
conn.delete_object(container, obj['name'])
|
|
except ClientException as e:
|
|
if e.http_status != 404:
|
|
print('DELETE of %s/%s failed with status '
|
|
'code %d' % (container, obj['name'],
|
|
e.http_status))
|
|
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.')
|