
Bug: https://bugs.launchpad.net/gluster-swift/+bug/1269444 Recommended way is to flush the cached tokens from memcached after gswauth-clean-up-token. As currently there is no way to flush only those tokens from memcached. So a debug message recommending restart of the memcached. A quick fix. Bigger fix would come in later. Older cached tokens should be flushed/invalidated to let access path work correctly with valid and new tokens. Change-Id: Ic7a820eb3c60bac4829d5c5230cb3a5241b77957 Signed-off-by: Chetan Risbud <crisbud@redhat.com> Reviewed-on: http://review.gluster.org/7168 Reviewed-by: Prashanth Pai <ppai@redhat.com> Tested-by: Prashanth Pai <ppai@redhat.com> Reviewed-by: pushpesh sharma <psharma@redhat.com> Tested-by: pushpesh sharma <psharma@redhat.com>
203 lines
8.9 KiB
Python
Executable File
203 lines
8.9 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.
|
|
|
|
try:
|
|
import simplejson as json
|
|
except ImportError:
|
|
import json
|
|
import gettext
|
|
import re
|
|
import socket
|
|
|
|
from datetime import datetime, timedelta
|
|
from optparse import OptionParser
|
|
from sys import argv, exit
|
|
from time import sleep, time
|
|
|
|
from swiftclient.client import Connection, ClientException
|
|
|
|
if __name__ == '__main__':
|
|
gettext.install('gswauth', 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 is required.')
|
|
parser.add_option('', '--purge', dest='purge_account', help='Purges all '
|
|
'tokens for a given account whether the tokens have expired or not.'
|
|
' Memcached restart is recommended. Old tokens may still persist in'
|
|
' memcached.')
|
|
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.'
|
|
' Memcached restart is recommended. Old tokens may still persist in'
|
|
' memcached.')
|
|
args = argv[1:]
|
|
if not args:
|
|
args.append('-h')
|
|
(options, args) = parser.parse_args(args)
|
|
if len(args) != 0:
|
|
parser.parse_args(['-h'])
|
|
if options.admin_key is None:
|
|
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'
|
|
|
|
try:
|
|
options.token_life = timedelta(0, float(options.token_life))
|
|
options.sleep = float(options.sleep)
|
|
except ValueError:
|
|
parser.parse_args(['-h'])
|
|
|
|
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)
|
|
try:
|
|
objs = conn.get_container(options.purge_account,
|
|
marker=marker)[1]
|
|
except ClientException, e:
|
|
if e.http_status == 404:
|
|
exit('Account %s not found.' % (options.purge_account))
|
|
elif e.http_status == 401:
|
|
exit('Cleanup tokens failed: 401 Unauthorized: ' \
|
|
'Invalid user/key provided')
|
|
else:
|
|
exit('Purging %s failed with status '
|
|
'code %d' % (options.purge_account, e.http_status))
|
|
except socket.error, (errno, msg):
|
|
exit('Token clean-up failed: %s. ' \
|
|
'Check that the admin_url is valid' % msg)
|
|
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, err:
|
|
if err.http_status != 404:
|
|
raise
|
|
continue
|
|
if options.verbose:
|
|
print 'Done.'
|
|
exit(0)
|
|
for x in xrange(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, e:
|
|
if e.http_status == 404:
|
|
exit('Container %s not found. gswauth-prep needs to be '
|
|
'rerun' % (container))
|
|
elif e.http_status == 401:
|
|
exit('Cleanup tokens failed: 401 Unauthorized: ' \
|
|
'Invalid user/key provided')
|
|
else:
|
|
exit('Object listing on container %s failed with status '
|
|
'code %d' % (container, e.http_status))
|
|
except socket.error, (errno, msg):
|
|
exit('Token clean-up failed: %s. ' \
|
|
'Check that the admin_url is valid' % msg)
|
|
|
|
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, err:
|
|
if err.http_status != 404:
|
|
raise
|
|
continue
|
|
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'])
|
|
try:
|
|
conn.delete_object(container, obj['name'])
|
|
except ClientException, 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.'
|
|
print 'Recommended to restart memcached as old invalid tokens may' \
|
|
' still persist in memcached.'
|