kayobe/ansible/roles/swift-rings/files/swift-ring-builder.py
Mark Goddard c847ef9157 Fix copying Swift ring files
Ring files are binary formatted and should not be templated. Attempting
to template them causes an error such as the following:

    Template source files must be utf-8 encoded

This change fixes the issue by implementing support for configuration
files that are listed as 'untemplated'.

Change-Id: I9c6b0d9d5e13e8b82ebb8e8a3a0f432efb865e28
Story: 2007297
Task: 38774
2020-12-15 16:55:31 +00:00

129 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""
Script to build a Swift ring from a declarative YAML configuration. This has
been built via a script to avoid repeated 'docker exec' commands which could
take a long time.
Usage:
python swift-ring-builder.py <config file path> <build path> <service name>
Example:
python swift-ring-builder.py /path/to/config.yml /path/to/builds object
Example configuration format:
---
part_power: 10
replication_count: 3
min_part_hours: 1
hosts:
- host: swift1
region: 1
zone: 1
ip: 10.0.0.1
port: 6001
replication_ip: 10.1.0.1
replication_port: 6001
devices:
- device: /dev/sdb
weight: 100
- device: /dev/sdc
weight: 100
"""
import subprocess
import sys
import yaml
class RingBuilder(object):
"""Helper class for building Swift rings."""
def __init__(self, build_path, service_name):
self.build_path = build_path
self.service_name = service_name
def get_base_command(self):
return [
'swift-ring-builder',
'%s/%s.builder' % (self.build_path, self.service_name),
]
def create(self, part_power, replication_count, min_part_hours):
cmd = self.get_base_command()
cmd += [
'create',
"{}".format(part_power),
"{}".format(replication_count),
"{}".format(min_part_hours),
]
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
print("Failed to create %s ring" % self.service_name)
sys.exit(1)
def add_device(self, host, device):
cmd = self.get_base_command()
cmd += [
'add',
'--region', "{}".format(host['region']),
'--zone', "{}".format(host['zone']),
'--ip', host['ip'],
'--port', "{}".format(host['port']),
'--replication-ip', host['replication_ip'],
'--replication-port', "{}".format(host['replication_port']),
'--device', device['device'],
'--weight', "{}".format(device['weight']),
]
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
print("Failed to add device %s on host %s to %s ring" %
(host['host'], device['device'], self.service_name))
sys.exit(1)
def rebalance(self):
cmd = self.get_base_command()
cmd += [
'rebalance',
]
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
print("Failed to rebalance %s ring" % self.service_name)
sys.exit(1)
def build_rings(config, build_path, service_name):
builder = RingBuilder(build_path, service_name)
builder.create(config['part_power'], config['replication_count'],
config['min_part_hours'])
for host in config['hosts']:
devices = host['devices']
# If no devices are present for this host, this will be None.
if devices is None:
continue
for device in devices:
builder.add_device(host, device)
builder.rebalance()
def main():
if len(sys.argv) != 4:
raise Exception("Usage: {0} <config file path> <build path> "
"<service name>")
config_path = sys.argv[1]
build_path = sys.argv[2]
service_name = sys.argv[3]
with open(config_path) as f:
config = yaml.safe_load(f)
build_rings(config, build_path, service_name)
if __name__ == "__main__":
main()