1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
"""
This script test if a Swift cluster ( or a swiftsore server)
is under replication
"""
import sys
import socket
import eventlet
from oslo_config import cfg
from swift.cli import recon
from swift.common.ring import RingBuilder
from urllib.parse import urlparse
# Initialize Swift connection
swiftrecon = recon.SwiftRecon()
pool_size = 30
pool = eventlet.GreenPool(pool_size)
CLI_OPTS = [
cfg.StrOpt('type', short='t', choices=['object', 'container', 'account'],
default='object', help='Choose de type of server'),
]
def _prepare_config():
"""
Prepare the oslo_config of scripts by analyse arguments
return: the oslo_config object
"""
CONF = cfg.ConfigOpts()
CONF.register_cli_opts(CLI_OPTS)
return CONF
def analyze_ring():
"""
Analyze the ring for retrieve informations about host or cluster
If script running in a switstore, he check only this server,
otherwise, he check all cluster
return: list off tuble with ip and port, total partitions on this hosts
parttion to be emptied ans filled
"""
hosts = {}
parts = 0
filling = {}
emptying = {}
devs = [d for d in builder.devs if d]
# If server have disk, use it, else use all swiftstore
ip = socket.gethostbyname(socket.gethostname())
devs_ip = [d for d in devs if d['ip'] == ip]
if devs_ip:
devs = devs_ip
for h in devs:
hosts[h['ip']] = h['port']
parts += h['parts']
if h['parts_wanted'] < -builder.parts:
# If emtying swiftstore, parts-wanted is
# (builder.parts * builder.replicas) -
# total.parts to rebalance + device parts to rebalance
# So is always < -builder.parts
emptying.setdefault(h['ip'], 0)
emptying[h['ip']] += h['parts']
elif h['parts_wanted'] < 0:
# If rebalance after add swifstore, this juste parts number
# in swiftstore that they are emptied
emptying.setdefault(h['ip'], 0)
emptying[h['ip']] -= h['parts_wanted']
elif h['parts_wanted'] > 0:
# In all case this number of part of device that theyr are filled
filling.setdefault(h['ip'], 0)
filling[h['ip']] += h['parts_wanted']
return set(hosts.items()), parts, filling, emptying
def get_attempted(hosts):
"""
Analyze all hosts
return: tuple with number of replication in progress
"""
scout = recon.Scout(recon_type=f"replication/{args.type}",
suppress_errors=True)
attempted = {}
for url, response, status, _ts_start, _ts_end \
in pool.imap(scout.scout, hosts):
if status == 200:
ip = urlparse(url).hostname
attempted[ip] = response['replication_stats']['attempted']
return sum(attempted.values())
def main():
"""
Entry point for the script
"""
global builder, args
args = _prepare_config()
try:
args(sys.argv[1:])
except cfg.RequiredOptError as E:
print(E)
args.print_usage()
return 1
try:
builder = RingBuilder.load(f"/etc/swift/{args.type}.builder")
except Exception as e:
sys.exit(e)
hosts, parts, filling, emptying = analyze_ring()
total_emptying = sum(emptying.values())
total_filling = sum(filling.values())
percent_emptying = total_emptying / parts
percent_filling = total_filling / parts
if total_emptying or total_filling:
print("Dispersion for the cluster is not optimal !")
print(f"{total_filling} parts remains to be filled \
({percent_filling:.2%}) on {len(filling)} hosts")
print(f"{total_emptying} parts remains to be emptied \
({percent_emptying:.2%}) on {len(emptying)} hosts")
print("You can re-run 'swift-ring-builder object.builder rebalance' \
AFTER the replication is OK !")
print("=" * 80)
attempted = get_attempted(hosts)
inProgress = abs(attempted - parts)
perCent = 1 - inProgress / parts
if inProgress:
print(f"Replication in progress: {inProgress} parts to sync \
({perCent:.2%} Ok) ")
else:
print("Replication OK")
if __name__ == "__main__":
sys.exit(main())
|