Add "normal", optparse-style options to swift-ring-builder add.
The old format is still present and works just like it did before, so your existing scripts won't break. New format pros: * it's readable even for Swift newcomers * it's easy to extend * it's familiar to anyone who's used a Unix command line * we don't have to maintain the parser New format cons: * you can't add multiple devices in one go Old format pros: * you can add many devices with one command * it's compact Old format cons: * it confuses newcomers * "wait, is that zone dash IP colon port slash device, or zone slash IP dash port colon meta underscore device?" Just try walking someone through adding a device over voice chat. * it's annoying to add new fields Note that this only affects the command "swift-ring-builder <builderfile> add". Other swift-ring-builder commands are unchanged. DocImpact Change-Id: I034b7f79eb6f4d81a5c4da193e1358741441c5b5
This commit is contained in:
parent
4077252f23
commit
d69f929be9
@ -14,6 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import optparse
|
||||
from array import array
|
||||
from errno import EEXIST
|
||||
from itertools import islice, izip
|
||||
@ -50,6 +51,185 @@ def format_device(dev):
|
||||
'"%(meta)s"' % copy_dev)
|
||||
|
||||
|
||||
def _parse_add_values(argvish):
|
||||
"""
|
||||
Parse devices to add as specified on the command line.
|
||||
|
||||
Will exit on error and spew warnings.
|
||||
|
||||
:returns: array of device dicts
|
||||
"""
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-r', '--region', type="int",
|
||||
help="Region")
|
||||
parser.add_option('-z', '--zone', type="int",
|
||||
help="Zone")
|
||||
parser.add_option('-i', '--ip', type="string",
|
||||
help="IP address")
|
||||
parser.add_option('-p', '--port', type="int",
|
||||
help="Port number")
|
||||
parser.add_option('-j', '--replication-ip', type="string",
|
||||
help="Replication IP address")
|
||||
parser.add_option('-q', '--replication-port', type="int",
|
||||
help="Replication port number")
|
||||
parser.add_option('-d', '--device', type="string",
|
||||
help="Device name (e.g. md0, sdb1)")
|
||||
parser.add_option('-w', '--weight', type="float",
|
||||
help="Device weight")
|
||||
parser.add_option('-m', '--meta', type="string", default="",
|
||||
help="Extra device info (just a string)")
|
||||
opts, args = parser.parse_args(argvish)
|
||||
|
||||
# We'll either parse the all-in-one-string format or the --options format,
|
||||
# but not both. If both are specified, raise an error.
|
||||
opts_used = opts.region or opts.zone or opts.ip or opts.port or \
|
||||
opts.device or opts.weight or opts.meta
|
||||
|
||||
if len(args) > 0 and opts_used:
|
||||
print Commands.add.__doc__.strip()
|
||||
exit(EXIT_ERROR)
|
||||
elif len(args) > 0:
|
||||
if len(args) % 2 != 0:
|
||||
print Commands.add.__doc__.strip()
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
parsed_devs = []
|
||||
devs_and_weights = izip(islice(args, 0, len(args), 2),
|
||||
islice(args, 1, len(args), 2))
|
||||
|
||||
for devstr, weightstr in devs_and_weights:
|
||||
region = 1
|
||||
rest = devstr
|
||||
if devstr.startswith('r'):
|
||||
i = 1
|
||||
while i < len(devstr) and devstr[i].isdigit():
|
||||
i += 1
|
||||
region = int(devstr[1:i])
|
||||
rest = devstr[i:]
|
||||
else:
|
||||
stderr.write("WARNING: No region specified for %s. "
|
||||
"Defaulting to region 1.\n" % devstr)
|
||||
|
||||
if not rest.startswith('z'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
zone = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith('-'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
if rest[i] == '[':
|
||||
i += 1
|
||||
while i < len(rest) and rest[i] != ']':
|
||||
i += 1
|
||||
i += 1
|
||||
ip = rest[1:i].lstrip('[').rstrip(']')
|
||||
rest = rest[i:]
|
||||
else:
|
||||
while i < len(rest) and rest[i] in '0123456789.':
|
||||
i += 1
|
||||
ip = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith(':'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
port = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
replication_ip = ip
|
||||
replication_port = port
|
||||
if rest.startswith('R'):
|
||||
i = 1
|
||||
if rest[i] == '[':
|
||||
i += 1
|
||||
while i < len(rest) and rest[i] != ']':
|
||||
i += 1
|
||||
i += 1
|
||||
replication_ip = rest[1:i].lstrip('[').rstrip(']')
|
||||
rest = rest[i:]
|
||||
else:
|
||||
while i < len(rest) and rest[i] in '0123456789.':
|
||||
i += 1
|
||||
replication_ip = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith(':'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
replication_port = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith('/'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i] != '_':
|
||||
i += 1
|
||||
device_name = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
meta = ''
|
||||
if rest.startswith('_'):
|
||||
meta = rest[1:]
|
||||
|
||||
try:
|
||||
weight = float(weightstr)
|
||||
except ValueError:
|
||||
print 'Invalid weight value: %s' % weightstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
if weight < 0:
|
||||
print 'Invalid weight value (must be positive): %s' % weightstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
parsed_devs.append({'region': region, 'zone': zone, 'ip': ip,
|
||||
'port': port, 'device': device_name,
|
||||
'replication_ip': replication_ip,
|
||||
'replication_port': replication_port,
|
||||
'weight': weight, 'meta': meta})
|
||||
return parsed_devs
|
||||
else:
|
||||
for attribute, shortopt, longopt in (['region', '-r', '--region'],
|
||||
['zone', '-z', '--zone'],
|
||||
['ip', '-i', '--ip'],
|
||||
['port', '-p', '--port'],
|
||||
['device', '-d', '--device'],
|
||||
['weight', '-w', '--weight']):
|
||||
if not getattr(opts, attribute, None):
|
||||
print 'Required argument %s/%s not specified.' % \
|
||||
(shortopt, longopt)
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
replication_ip = getattr(opts, 'replication_ip', opts.ip)
|
||||
replication_port = getattr(opts, 'replication_port', opts.port)
|
||||
|
||||
return [{'region': opts.region, 'zone': opts.zone, 'ip': opts.ip,
|
||||
'port': opts.port, 'device': opts.device, 'meta': opts.meta,
|
||||
'replication_ip': replication_ip,
|
||||
'replication_port': replication_port,
|
||||
'weight': opts.weight}]
|
||||
|
||||
|
||||
class Commands:
|
||||
|
||||
def unknown():
|
||||
@ -208,6 +388,13 @@ swift-ring-builder <builder_file> add
|
||||
|
||||
Where <r_ip> and <r_port> are replication ip and port.
|
||||
|
||||
or
|
||||
|
||||
swift-ring-builder <builder_file> add
|
||||
[--region <region>] --zone <zone> --ip <ip> --port <port>
|
||||
--replication-ip <r_ip> --replication-port <r_port>
|
||||
--device <device_name> --meta <meta> --weight <weight>
|
||||
|
||||
Adds devices to the ring with the given information. No partitions will be
|
||||
assigned to the new device until after running 'rebalance'. This is so you
|
||||
can make multiple device changes and rebalance them all just once.
|
||||
@ -216,128 +403,21 @@ swift-ring-builder <builder_file> add
|
||||
print Commands.add.__doc__.strip()
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
devs_and_weights = izip(islice(argv, 3, len(argv), 2),
|
||||
islice(argv, 4, len(argv), 2))
|
||||
for devstr, weightstr in devs_and_weights:
|
||||
region = 1
|
||||
rest = devstr
|
||||
if devstr.startswith('r'):
|
||||
i = 1
|
||||
while i < len(devstr) and devstr[i].isdigit():
|
||||
i += 1
|
||||
region = int(devstr[1:i])
|
||||
rest = devstr[i:]
|
||||
else:
|
||||
stderr.write("WARNING: No region specified for %s. "
|
||||
"Defaulting to region 1.\n" % devstr)
|
||||
|
||||
if not rest.startswith('z'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
zone = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith('-'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
if rest[i] == '[':
|
||||
i += 1
|
||||
while i < len(rest) and rest[i] != ']':
|
||||
i += 1
|
||||
i += 1
|
||||
ip = rest[1:i].lstrip('[').rstrip(']')
|
||||
rest = rest[i:]
|
||||
else:
|
||||
while i < len(rest) and rest[i] in '0123456789.':
|
||||
i += 1
|
||||
ip = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith(':'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
port = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
replication_ip = ip
|
||||
replication_port = port
|
||||
if rest.startswith('R'):
|
||||
i = 1
|
||||
if rest[i] == '[':
|
||||
i += 1
|
||||
while i < len(rest) and rest[i] != ']':
|
||||
i += 1
|
||||
i += 1
|
||||
replication_ip = rest[1:i].lstrip('[').rstrip(']')
|
||||
rest = rest[i:]
|
||||
else:
|
||||
while i < len(rest) and rest[i] in '0123456789.':
|
||||
i += 1
|
||||
replication_ip = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith(':'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i].isdigit():
|
||||
i += 1
|
||||
replication_port = int(rest[1:i])
|
||||
rest = rest[i:]
|
||||
|
||||
if not rest.startswith('/'):
|
||||
print 'Invalid add value: %s' % devstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
i = 1
|
||||
while i < len(rest) and rest[i] != '_':
|
||||
i += 1
|
||||
device_name = rest[1:i]
|
||||
rest = rest[i:]
|
||||
|
||||
meta = ''
|
||||
if rest.startswith('_'):
|
||||
meta = rest[1:]
|
||||
|
||||
try:
|
||||
weight = float(weightstr)
|
||||
except ValueError:
|
||||
print 'Invalid weight value: %s' % weightstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
if weight < 0:
|
||||
print 'Invalid weight value (must be positive): %s' % weightstr
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
|
||||
for new_dev in _parse_add_values(argv[3:]):
|
||||
for dev in builder.devs:
|
||||
if dev is None:
|
||||
continue
|
||||
if dev['ip'] == ip and dev['port'] == port and \
|
||||
dev['device'] == device_name:
|
||||
if dev['ip'] == new_dev['ip'] and \
|
||||
dev['port'] == new_dev['port'] and \
|
||||
dev['device'] == new_dev['device']:
|
||||
print 'Device %d already uses %s:%d/%s.' % \
|
||||
(dev['id'], dev['ip'], dev['port'], dev['device'])
|
||||
print "The on-disk ring builder is unchanged.\n"
|
||||
exit(EXIT_ERROR)
|
||||
dev_params = {'region': region, 'zone': zone, 'ip': ip,
|
||||
'port': port, 'replication_ip': replication_ip,
|
||||
'replication_port': replication_port,
|
||||
'device': device_name, 'weight': weight,
|
||||
'meta': meta}
|
||||
dev_id = builder.add_dev(dev_params)
|
||||
dev_id = builder.add_dev(new_dev)
|
||||
print('Device %s with %s weight got id %s' %
|
||||
(format_device(dev_params), weight, dev_id))
|
||||
(format_device(new_dev), new_dev['weight'], dev_id))
|
||||
|
||||
builder.save(argv[1])
|
||||
exit(EXIT_SUCCESS)
|
||||
|
||||
|
@ -114,7 +114,9 @@ Shows information about matching devices.
|
||||
.RE
|
||||
|
||||
|
||||
.IP "\fBadd\fR z<zone>-<ip>:<port>/<device_name>_<meta><wght>"
|
||||
.IP "\fBadd\fR z<zone>-<ip>:<port>/<device_name>_<meta> <weight>"
|
||||
.IP "\fBadd\fR r<region>z<zone>-<ip>:<port>/<device_name>_<meta> <weight>"
|
||||
.IP "\fBadd\fR -r <region> -z <zone> -i <ip> -p <port> -d <device_name> -m <meta> -w <weight>"
|
||||
.RS 5
|
||||
Adds a device to the ring with the given information. No partitions will be
|
||||
assigned to the new device until after running 'rebalance'. This is so you
|
||||
|
Loading…
Reference in New Issue
Block a user