File: check_replication.py

package info (click to toggle)
swift-tools 0.0.24
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 184 kB
  • sloc: python: 1,105; sh: 168; makefile: 14
file content (135 lines) | stat: -rw-r--r-- 4,305 bytes parent folder | download
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())