--- /dev/null
+import argparse
+import logging
+import sys
+
+import teuthology
+from teuthology.lock import query, ops
+
+def main():
+ args = parse_args(sys.argv[1:])
+ if args.verbose:
+ teuthology.log.setLevel(logging.DEBUG)
+ log = logging.getLogger(__name__)
+ stale = query.find_stale_locks(args.owner)
+ if not stale:
+ return
+ if args.dry_run:
+ log.info("Would attempt to unlock:")
+ for node in stale:
+ log.info(f"{node['name']}\t{node['description']}")
+ else:
+ names = [node["name"] for node in stale]
+ ops.unlock_safe(names, args.owner)
+
+def parse_args(argv):
+ parser = argparse.ArgumentParser(
+ description="Find and unlock nodes that are still locked by jobs that are no "
+ "longer active",
+ )
+ parser.add_argument(
+ '-v', '--verbose',
+ action='store_true',
+ default=False,
+ help='Be more verbose',
+ )
+ parser.add_argument(
+ '--dry-run',
+ action='store_true',
+ default=False,
+ help="List nodes that would be unlocked if the flag were omitted",
+ )
+ parser.add_argument(
+ '--owner',
+ help='Optionally, find nodes locked by a specific user',
+ )
+ return parser.parse_args(argv)
+
+if __name__ == "__main__":
+ main()
teuthology-dispatcher = scripts.dispatcher:main
teuthology-wait = scripts.wait:main
teuthology-exporter = scripts.exporter:main
+ teuthology-node-cleanup = scripts.node_cleanup:main
[options.extras_require]
manhole =