#!/usr/bin/env python # Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import time import jenkins def delete_offline_nodes(server, timeout): start = time.time() end = start + timeout while True: nodes = server.get_nodes() offline_nodes = [node for node in nodes if node['offline']] if not offline_nodes: # We have converged to no offline nodes state break elif time.time() > end: raise Exception("Offline slave deletion timeout exceeded.") try: for n in offline_nodes: server.delete_node(n['name']) # This may have raced an external system deleting nodes # after jobs complete. Just retry as we should # converage on no jobs running and no deletions. except jenkins.NotFoundException: pass except jenkins.JenkinsException as e: if '[500]' in e.message: pass else: raise time.sleep(1) def parse_args(): parser = argparse.ArgumentParser(description='Safely stop Jenkins.') parser.add_argument('--url', default='http://localhost:8080', help='Base url for Jenkins master.') parser.add_argument('--user', required=True, help='Username to connect to Jenkins with.') parser.add_argument('--password', required=True, help='Password to auth with.') parser.add_argument('--no-delete', dest='delete', action='store_false', default=True, help="Don't delete offline slaves.") parser.add_argument('--delete-timeout', type=int, default=300, help="Seconds to spend deleting offline slaves.") args = parser.parse_args() return args def main(): args = parse_args() server = jenkins.Jenkins(args.url, username=args.user, password=args.password) # Put in shutdown mode server.quiet_down() while True: try: if not server.get_running_builds(): break # This may have raced an external system deleting executors # while listing the running jobs. Just retry as we should # converage on no jobs running and no deletions. except jenkins.NotFoundException: pass except jenkins.JenkinsException as e: if '[500]' in e.message: pass else: raise # Jobs are slow wait a minute between polls time.sleep(60) if args.delete: # Remove any offline nodes so they don't go online after restart. delete_offline_nodes(server, args.delete_timeout) if __name__ == '__main__': main()