]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
Add command line tool for locking machines.
authorJosh Durgin <josh.durgin@dreamhost.com>
Sat, 2 Jul 2011 01:18:03 +0000 (18:18 -0700)
committerJosh Durgin <josh.durgin@dreamhost.com>
Thu, 7 Jul 2011 19:35:08 +0000 (12:35 -0700)
requirements.txt
setup.py
teuthology/lock.py [new file with mode: 0644]

index 144d6808008204175e0c7e78dddd5d30b724b1ac..bd811828a5f82478eaa8535f85ef2c3d749355b6 100644 (file)
@@ -3,3 +3,4 @@ configobj
 PyYAML
 bunch >=1.0.0
 argparse >=1.2.1
+httplib2
index 64c8ac9f52ed6c54ff9b27142e8b72a92f66c8d1..d78a1c6ce868e42522c5cf90300e1a8d258ba0cb 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -26,6 +26,7 @@ setup(
             'teuthology-nuke = teuthology.run:nuke',
             'teuthology-suite = teuthology.suite:main',
             'teuthology-ls = teuthology.suite:ls',
+            'teuthology-lock = teuthology.lock:main',
             ],
         },
 
diff --git a/teuthology/lock.py b/teuthology/lock.py
new file mode 100644 (file)
index 0000000..96fa3ca
--- /dev/null
@@ -0,0 +1,226 @@
+import argparse
+import httplib2
+import json
+import logging
+import os
+import urllib
+
+from teuthology import misc as teuthology
+
+log = logging.getLogger(__name__)
+
+def _lock_url():
+    return os.getenv('TEUTHOLOGY_LOCK_SERVER', 'http://localhost:8080/lock')
+
+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(num, user=None):
+    if user is None:
+        user = teuthology.get_user()
+    success, content = send_request('POST', _lock_url(),
+                                    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(name, user=None):
+    if user is None:
+        user = teuthology.get_user()
+    success, _ = send_request('POST', _lock_url() + '/' + 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(name, user=None):
+    if user is None:
+        user = teuthology.get_user()
+    success, _ = send_request('DELETE', _lock_url() + '/' + 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(name):
+    success, content = send_request('GET', _lock_url() + '/' + name)
+    if success:
+        return json.loads(content)
+    return None
+
+def list_locks():
+    success, content = send_request('GET', _lock_url())
+    if success:
+        return json.loads(content)
+    return None
+
+def update_lock(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() + '/' + 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(
+        '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,
+        )
+
+    ret = 0
+    user = ctx.owner
+    machines = ctx.machines
+    machines_to_update = []
+
+    if ctx.list:
+        if ctx.status is not None or ctx.desc is not None:
+            log.error('--up and --desc do nothing with --list')
+            return 1
+
+        if machines:
+            statuses = [get_status(machine) for machine in machines]
+        else:
+            statuses = list_locks()
+
+        if statuses:
+            print json.dumps(statuses, indent=4)
+        else:
+            log.error('error retrieving lock statuses')
+            ret = 1
+    elif ctx.lock:
+        assert machines, 'You must specify machines to lock.'
+        for machine in machines:
+            if not lock(machine, user):
+                ret = 1
+                if not ctx.f:
+                    return ret
+            else:
+                machines_to_update.append(machine)
+    elif ctx.unlock:
+        assert machines, 'You must specify machines to unlock.'
+        for machine in machines:
+            if not unlock(machine, user):
+                ret = 1
+                if not ctx.f:
+                    return ret
+            else:
+                machines_to_update.append(machine)
+    elif ctx.num_to_lock:
+        assert not machines, \
+            'The --lock-many option does not support specifying machines'
+        result = lock_many(ctx.num_to_lock, user)
+        if not result:
+            ret = 1
+        else:
+            machines_to_update = result
+            print json.dumps(result, indent=4)
+    elif ctx.update:
+        assert machines, 'You must specify machines to update'
+        machines_to_update = machines
+
+    if ctx.desc is not None or ctx.status is not None:
+        for machine in machines_to_update:
+            update_lock(machine, ctx.desc, ctx.status)
+
+    return ret