ceph/teuthology/lock.py

254 lines
7.3 KiB
Python
Raw Normal View History

import argparse
import httplib2
import json
import logging
import urllib
import yaml
from teuthology import misc as teuthology
log = logging.getLogger(__name__)
def _lock_url(ctx):
return ctx.teuthology_config['lock_server']
def send_request(method, url, body=None):
http = httplib2.Http()
resp, content = http.request(url, method=method, body=body)
if resp.status == 200:
return (True, content)
log.info("%s request to '%s' with body '%s' failed with response code %d",
method, url, body, resp.status)
return (False, None)
def lock_many(ctx, num, user=None):
if user is None:
user = teuthology.get_user()
success, content = send_request('POST', _lock_url(ctx),
urllib.urlencode(dict(user=user, num=num)))
if success:
machines = json.loads(content)
log.debug('locked {machines}'.format(machines=', '.join(machines)))
return machines
log.warn('Could not lock %d nodes', num)
return []
def lock(ctx, name, user=None):
if user is None:
user = teuthology.get_user()
success, _ = send_request('POST', _lock_url(ctx) + '/' + name,
urllib.urlencode(dict(user=user)))
if success:
log.debug('locked %s as %s', name, user)
else:
log.error('failed to lock %s', name)
return success
def unlock(ctx, name, user=None):
if user is None:
user = teuthology.get_user()
success, _ = send_request('DELETE', _lock_url(ctx) + '/' + name + '?' + \
urllib.urlencode(dict(user=user)))
if success:
log.debug('unlocked %s', name)
else:
log.error('failed to unlock %s', name)
return success
def get_status(ctx, name):
success, content = send_request('GET', _lock_url(ctx) + '/' + name)
if success:
return json.loads(content)
return None
def list_locks(ctx):
success, content = send_request('GET', _lock_url(ctx))
if success:
return json.loads(content)
return None
def update_lock(ctx, name, description=None, status=None):
updated = {}
if description is not None:
updated['desc'] = description
if status is not None:
updated['status'] = status
if updated:
success, _ = send_request('PUT', _lock_url(ctx) + '/' + name + '?' + \
urllib.urlencode(updated))
return success
return True
def _positive_int(string):
value = int(string)
if value < 1:
raise argparse.ArgumentTypeError(
'{string} is not positive'.format(string=string))
return value
def main():
parser = argparse.ArgumentParser(description="""
Lock, unlock, or query lock status of machines.
""")
parser.add_argument(
'-v', '--verbose',
action='store_true',
default=False,
help='be more verbose',
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--list',
action='store_true',
default=False,
help='show lock info for all machines, or only machines specified',
)
group.add_argument(
'--lock',
action='store_true',
default=False,
help='lock particular machines',
)
group.add_argument(
'--unlock',
action='store_true',
default=False,
help='unlock particular machines',
)
group.add_argument(
'--lock-many',
dest='num_to_lock',
type=_positive_int,
help='lock this many machines',
)
group.add_argument(
'--update',
action='store_true',
default=False,
help='update the description or status of some machines',
)
parser.add_argument(
'--owner',
default=None,
help='who will own locked machines',
)
parser.add_argument(
'-f',
action='store_true',
default=False,
help='don\'t exit after the first error, continue locking or unlocking other machines',
)
parser.add_argument(
'--desc',
default=None,
help='update description',
)
parser.add_argument(
'--status',
default=None,
choices=['up', 'down'],
help='update status',
)
parser.add_argument(
'-t', '--targets',
dest='targets',
default=None,
2011-07-12 01:00:03 +00:00
help='input yaml containing targets',
)
parser.add_argument(
'machines',
metavar='MACHINE',
default=[],
nargs='*',
help='machines to operate on',
)
ctx = parser.parse_args()
loglevel = logging.ERROR
if ctx.verbose:
loglevel = logging.DEBUG
logging.basicConfig(
level=loglevel,
)
teuthology.read_config(ctx)
ret = 0
user = ctx.owner
machines = ctx.machines
machines_to_update = []
2011-07-12 01:00:03 +00:00
if ctx.targets:
try:
with file(ctx.targets) as f:
g = yaml.safe_load_all(f)
for new in g:
if 'targets' in new:
for t in new['targets']:
2011-07-12 01:00:03 +00:00
machines.append(t)
except IOError, e:
raise argparse.ArgumentTypeError(str(e))
if ctx.f:
assert ctx.lock or ctx.unlock, \
'-f is only supported by --lock and --unlock'
if ctx.machines:
assert ctx.lock or ctx.unlock or ctx.list or ctx.update, \
'machines cannot be specified with that operation'
else:
assert ctx.num_to_lock or ctx.list, \
'machines must be specified for that operation'
if ctx.list:
assert ctx.status is None and ctx.desc is None, \
'--status and --desc do nothing with --list'
assert ctx.owner is None, 'the owner option does nothing with --list'
if machines:
statuses = [get_status(ctx, machine) for machine in machines]
else:
statuses = list_locks(ctx)
if statuses:
print json.dumps(statuses, indent=4)
else:
log.error('error retrieving lock statuses')
ret = 1
elif ctx.lock:
for machine in machines:
if not lock(ctx, machine, user):
ret = 1
if not ctx.f:
return ret
else:
machines_to_update.append(machine)
elif ctx.unlock:
for machine in machines:
if not unlock(ctx, machine, user):
ret = 1
if not ctx.f:
return ret
else:
machines_to_update.append(machine)
elif ctx.num_to_lock:
result = lock_many(ctx, ctx.num_to_lock, user)
if not result:
ret = 1
else:
machines_to_update = result
2011-07-12 01:00:03 +00:00
print yaml.safe_dump(dict(targets=result), default_flow_style=False)
elif ctx.update:
assert ctx.desc is not None or ctx.status is not None, \
'you must specify description or status to update'
assert ctx.owner is None, 'only description and status may be updated'
machines_to_update = machines
if ctx.desc is not None or ctx.status is not None:
for machine in machines_to_update:
update_lock(ctx, machine, ctx.desc, ctx.status)
return ret