summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorChristian Schwede <cschwede@redhat.com>2017-06-23 16:34:10 +0200
committerTim Burke <tim.burke@gmail.com>2017-09-13 05:56:34 +0000
commitb77de5393f540973685095cf087b3f839a55ba9f (patch)
treebcdcc99d961692c3123ed8608f8dfdee9aa88b76 /bin
parent097a9083eced36b69d4edbf1fc6130ca8b04bca3 (diff)
downloadswift-b77de5393f540973685095cf087b3f839a55ba9f.tar.gz
Make swift-dispersion-report importable
This patch allows to import the dispersion report tool, and thus making it more easily usable within other Python tools. This can be also used in a follow up patch to add some tests for the report tool. It also fixes a bug when using the "--dump-json" option - until now it returned the policy name and made the JSON invalid. Change-Id: Ie0d52a1a54fc152bb72cbb3f84dcc36a8dad972a
Diffstat (limited to 'bin')
-rwxr-xr-xbin/swift-dispersion-report411
1 files changed, 0 insertions, 411 deletions
diff --git a/bin/swift-dispersion-report b/bin/swift-dispersion-report
deleted file mode 100755
index 3a7270314..000000000
--- a/bin/swift-dispersion-report
+++ /dev/null
@@ -1,411 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2010-2012 OpenStack Foundation
-#
-# 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 __future__ import print_function
-import json
-from collections import defaultdict
-from six.moves.configparser import ConfigParser
-from optparse import OptionParser
-from sys import exit, stdout, stderr
-from time import time
-
-from eventlet import GreenPool, hubs, patcher, Timeout
-from eventlet.pools import Pool
-
-from swift.common import direct_client
-try:
- from swiftclient import get_auth
-except ImportError:
- from swift.common.internal_client import get_auth
-from swift.common.internal_client import SimpleClient
-from swift.common.ring import Ring
-from swift.common.exceptions import ClientException
-from swift.common.utils import compute_eta, get_time_units, config_true_value
-from swift.common.storage_policy import POLICIES
-
-
-unmounted = []
-notfound = []
-json_output = False
-debug = False
-insecure = False
-
-
-def get_error_log(prefix):
-
- def error_log(msg_or_exc):
- global debug, unmounted, notfound
- if hasattr(msg_or_exc, 'http_status'):
- identifier = '%s:%s/%s' % (msg_or_exc.http_host,
- msg_or_exc.http_port,
- msg_or_exc.http_device)
- if msg_or_exc.http_status == 507:
- if identifier not in unmounted:
- unmounted.append(identifier)
- print('ERROR: %s is unmounted -- This will '
- 'cause replicas designated for that device to be '
- 'considered missing until resolved or the ring is '
- 'updated.' % (identifier), file=stderr)
- stderr.flush()
- if debug and identifier not in notfound:
- notfound.append(identifier)
- print('ERROR: %s returned a 404' % (identifier), file=stderr)
- stderr.flush()
- if not hasattr(msg_or_exc, 'http_status') or \
- msg_or_exc.http_status not in (404, 507):
- print('ERROR: %s: %s' % (prefix, msg_or_exc), file=stderr)
- stderr.flush()
- return error_log
-
-
-def container_dispersion_report(coropool, connpool, account, container_ring,
- retries, output_missing_partitions, policy):
- with connpool.item() as conn:
- containers = [c['name'] for c in conn.get_account(
- prefix='dispersion_%d' % policy.idx, full_listing=True)[1]]
- containers_listed = len(containers)
- if not containers_listed:
- print('No containers to query. Has '
- 'swift-dispersion-populate been run?', file=stderr)
- stderr.flush()
- return
- retries_done = [0]
- containers_queried = [0]
- container_copies_missing = defaultdict(int)
- container_copies_found = [0]
- container_copies_expected = [0]
- begun = time()
- next_report = [time() + 2]
-
- def direct(container, part, nodes):
- found_count = 0
- for node in nodes:
- error_log = get_error_log('%(ip)s:%(port)s/%(device)s' % node)
- try:
- attempts, _junk = direct_client.retry(
- direct_client.direct_head_container, node, part, account,
- container, error_log=error_log, retries=retries)
- retries_done[0] += attempts - 1
- found_count += 1
- except ClientException as err:
- if err.http_status not in (404, 507):
- error_log('Giving up on /%s/%s/%s: %s' % (part, account,
- container, err))
- except (Exception, Timeout) as err:
- error_log('Giving up on /%s/%s/%s: %s' % (part, account,
- container, err))
- if output_missing_partitions and \
- found_count < len(nodes):
- missing = len(nodes) - found_count
- print('\r\x1B[K', end='')
- stdout.flush()
- print('# Container partition %s missing %s cop%s' % (
- part, missing, 'y' if missing == 1 else 'ies'), file=stderr)
- container_copies_found[0] += found_count
- containers_queried[0] += 1
- container_copies_missing[len(nodes) - found_count] += 1
- if time() >= next_report[0]:
- next_report[0] = time() + 5
- eta, eta_unit = compute_eta(begun, containers_queried[0],
- containers_listed)
- if not json_output:
- print('\r\x1B[KQuerying containers: %d of %d, %d%s left, %d '
- 'retries' % (containers_queried[0], containers_listed,
- round(eta), eta_unit, retries_done[0]),
- end='')
- stdout.flush()
- container_parts = {}
- for container in containers:
- part, nodes = container_ring.get_nodes(account, container)
- if part not in container_parts:
- container_copies_expected[0] += len(nodes)
- container_parts[part] = part
- coropool.spawn(direct, container, part, nodes)
- coropool.waitall()
- distinct_partitions = len(container_parts)
- copies_found = container_copies_found[0]
- copies_expected = container_copies_expected[0]
- value = 100.0 * copies_found / copies_expected
- elapsed, elapsed_unit = get_time_units(time() - begun)
- container_copies_missing.pop(0, None)
- if not json_output:
- print('\r\x1B[KQueried %d containers for dispersion reporting, '
- '%d%s, %d retries' % (containers_listed, round(elapsed),
- elapsed_unit, retries_done[0]))
- if containers_listed - distinct_partitions:
- print('There were %d overlapping partitions' % (
- containers_listed - distinct_partitions))
- for missing_copies, num_parts in container_copies_missing.items():
- print(missing_string(num_parts, missing_copies,
- container_ring.replica_count))
- print('%.02f%% of container copies found (%d of %d)' % (
- value, copies_found, copies_expected))
- print('Sample represents %.02f%% of the container partition space' % (
- 100.0 * distinct_partitions / container_ring.partition_count))
- stdout.flush()
- return None
- else:
- results = {'retries': retries_done[0],
- 'overlapping': containers_listed - distinct_partitions,
- 'pct_found': value,
- 'copies_found': copies_found,
- 'copies_expected': copies_expected}
- for missing_copies, num_parts in container_copies_missing.items():
- results['missing_%d' % (missing_copies)] = num_parts
- return results
-
-
-def object_dispersion_report(coropool, connpool, account, object_ring,
- retries, output_missing_partitions, policy):
- container = 'dispersion_objects_%d' % policy.idx
- with connpool.item() as conn:
- try:
- objects = [o['name'] for o in conn.get_container(
- container, prefix='dispersion_', full_listing=True)[1]]
- except ClientException as err:
- if err.http_status != 404:
- raise
-
- print('No objects to query. Has '
- 'swift-dispersion-populate been run?', file=stderr)
- stderr.flush()
- return
- objects_listed = len(objects)
- if not objects_listed:
- print('No objects to query. Has swift-dispersion-populate '
- 'been run?', file=stderr)
- stderr.flush()
- return
- retries_done = [0]
- objects_queried = [0]
- object_copies_found = [0]
- object_copies_expected = [0]
- object_copies_missing = defaultdict(int)
- begun = time()
- next_report = [time() + 2]
-
- headers = None
- if policy is not None:
- headers = {}
- headers['X-Backend-Storage-Policy-Index'] = int(policy)
-
- def direct(obj, part, nodes):
- found_count = 0
- for node in nodes:
- error_log = get_error_log('%(ip)s:%(port)s/%(device)s' % node)
- try:
- attempts, _junk = direct_client.retry(
- direct_client.direct_head_object, node, part, account,
- container, obj, error_log=error_log, retries=retries,
- headers=headers)
- retries_done[0] += attempts - 1
- found_count += 1
- except ClientException as err:
- if err.http_status not in (404, 507):
- error_log('Giving up on /%s/%s/%s/%s: %s' % (part, account,
- container, obj, err))
- except (Exception, Timeout) as err:
- error_log('Giving up on /%s/%s/%s/%s: %s' % (part, account,
- container, obj, err))
- if output_missing_partitions and \
- found_count < len(nodes):
- missing = len(nodes) - found_count
- print('\r\x1B[K', end='')
- stdout.flush()
- print('# Object partition %s missing %s cop%s' % (
- part, missing, 'y' if missing == 1 else 'ies'), file=stderr)
- object_copies_found[0] += found_count
- object_copies_missing[len(nodes) - found_count] += 1
- objects_queried[0] += 1
- if time() >= next_report[0]:
- next_report[0] = time() + 5
- eta, eta_unit = compute_eta(begun, objects_queried[0],
- objects_listed)
- if not json_output:
- print('\r\x1B[KQuerying objects: %d of %d, %d%s left, %d '
- 'retries' % (objects_queried[0], objects_listed,
- round(eta), eta_unit, retries_done[0]),
- end='')
- stdout.flush()
- object_parts = {}
- for obj in objects:
- part, nodes = object_ring.get_nodes(account, container, obj)
- if part not in object_parts:
- object_copies_expected[0] += len(nodes)
- object_parts[part] = part
- coropool.spawn(direct, obj, part, nodes)
- coropool.waitall()
- distinct_partitions = len(object_parts)
- copies_found = object_copies_found[0]
- copies_expected = object_copies_expected[0]
- value = 100.0 * copies_found / copies_expected
- elapsed, elapsed_unit = get_time_units(time() - begun)
- if not json_output:
- print('\r\x1B[KQueried %d objects for dispersion reporting, '
- '%d%s, %d retries' % (objects_listed, round(elapsed),
- elapsed_unit, retries_done[0]))
- if objects_listed - distinct_partitions:
- print('There were %d overlapping partitions' % (
- objects_listed - distinct_partitions))
-
- for missing_copies, num_parts in object_copies_missing.items():
- print(missing_string(num_parts, missing_copies,
- object_ring.replica_count))
-
- print('%.02f%% of object copies found (%d of %d)' %
- (value, copies_found, copies_expected))
- print('Sample represents %.02f%% of the object partition space' % (
- 100.0 * distinct_partitions / object_ring.partition_count))
- stdout.flush()
- return None
- else:
- results = {'retries': retries_done[0],
- 'overlapping': objects_listed - distinct_partitions,
- 'pct_found': value,
- 'copies_found': copies_found,
- 'copies_expected': copies_expected}
-
- for missing_copies, num_parts in object_copies_missing.items():
- results['missing_%d' % (missing_copies,)] = num_parts
- return results
-
-
-def missing_string(partition_count, missing_copies, copy_count):
- exclamations = ''
- missing_string = str(missing_copies)
- if missing_copies == copy_count:
- exclamations = '!!! '
- missing_string = 'all'
- elif copy_count - missing_copies == 1:
- exclamations = '! '
-
- verb_string = 'was'
- partition_string = 'partition'
- if partition_count > 1:
- verb_string = 'were'
- partition_string = 'partitions'
-
- copy_string = 'copies'
- if missing_copies == 1:
- copy_string = 'copy'
-
- return '%sThere %s %d %s missing %s %s.' % (
- exclamations, verb_string, partition_count, partition_string,
- missing_string, copy_string
- )
-
-
-if __name__ == '__main__':
- patcher.monkey_patch()
- hubs.get_hub().debug_exceptions = False
-
- conffile = '/etc/swift/dispersion.conf'
-
- parser = OptionParser(usage='''
-Usage: %%prog [options] [conf_file]
-
-[conf_file] defaults to %s'''.strip() % conffile)
- parser.add_option('-j', '--dump-json', action='store_true', default=False,
- help='dump dispersion report in json format')
- parser.add_option('-d', '--debug', action='store_true', default=False,
- help='print 404s to standard error')
- parser.add_option('-p', '--partitions', action='store_true', default=False,
- help='print missing partitions to standard error')
- parser.add_option('--container-only', action='store_true', default=False,
- help='Only run container report')
- parser.add_option('--object-only', action='store_true', default=False,
- help='Only run object report')
- parser.add_option('--insecure', action='store_true', default=False,
- help='Allow accessing insecure keystone server. '
- 'The keystone\'s certificate will not be verified.')
- parser.add_option('-P', '--policy-name', dest='policy_name',
- help="Specify storage policy name")
-
- options, args = parser.parse_args()
-
- if args:
- conffile = args.pop(0)
-
- c = ConfigParser()
- if not c.read(conffile):
- exit('Unable to read config file: %s' % conffile)
- conf = dict(c.items('dispersion'))
-
- if options.policy_name is None:
- policy = POLICIES.default
- else:
- policy = POLICIES.get_by_name(options.policy_name)
- if policy is None:
- exit('Unable to find policy: %s' % options.policy_name)
- print('Using storage policy: %s ' % policy.name)
-
- swift_dir = conf.get('swift_dir', '/etc/swift')
- retries = int(conf.get('retries', 5))
- concurrency = int(conf.get('concurrency', 25))
- endpoint_type = str(conf.get('endpoint_type', 'publicURL'))
- region_name = str(conf.get('region_name', ''))
- if options.dump_json or config_true_value(conf.get('dump_json', 'no')):
- json_output = True
- container_report = config_true_value(conf.get('container_report', 'yes')) \
- and not options.object_only
- object_report = config_true_value(conf.get('object_report', 'yes')) \
- and not options.container_only
- if not (object_report or container_report):
- exit("Neither container or object report is set to run")
- user_domain_name = str(conf.get('user_domain_name', ''))
- project_domain_name = str(conf.get('project_domain_name', ''))
- project_name = str(conf.get('project_name', ''))
- insecure = options.insecure \
- or config_true_value(conf.get('keystone_api_insecure', 'no'))
- if options.debug:
- debug = True
-
- coropool = GreenPool(size=concurrency)
-
- os_options = {'endpoint_type': endpoint_type}
- if user_domain_name:
- os_options['user_domain_name'] = user_domain_name
- if project_domain_name:
- os_options['project_domain_name'] = project_domain_name
- if project_name:
- os_options['project_name'] = project_name
- if region_name:
- os_options['region_name'] = region_name
-
- url, token = get_auth(conf['auth_url'], conf['auth_user'],
- conf['auth_key'],
- auth_version=conf.get('auth_version', '1.0'),
- os_options=os_options,
- insecure=insecure)
- account = url.rsplit('/', 1)[1]
- connpool = Pool(max_size=concurrency)
- connpool.create = lambda: SimpleClient(
- url=url, token=token, retries=retries)
-
- container_ring = Ring(swift_dir, ring_name='container')
- object_ring = Ring(swift_dir, ring_name=policy.ring_name)
-
- output = {}
- if container_report:
- output['container'] = container_dispersion_report(
- coropool, connpool, account, container_ring, retries,
- options.partitions, policy)
- if object_report:
- output['object'] = object_dispersion_report(
- coropool, connpool, account, object_ring, retries,
- options.partitions, policy)
- if json_output:
- print(json.dumps(output))