def get_running_builds(server: Jenkins): # running_builds = [] # get all running builds so we know if a duplicate job is already running running_builds = server.get_running_builds() queued_builds = server.get_queue_info() builds_with_params = [] for build in queued_builds: if "task" in build and "name" in build["task"]: try: parameters = parameters_from_actions(build["actions"]) builds_with_params.append({ "name": build["task"]["name"], "parameters": parameters }) except Exception: traceback.print_exc() for build in running_builds: try: parameters = parameters_for_job(server, build['name'], build['number']) builds_with_params.append({ "name": build['name'], "number": build['number'], "parameters": parameters }) except Exception: traceback.print_exc() return builds_with_params
class JenkinsBot(BotPlugin): """JenkinsBot is an Err plugin to manage Jenkins CI jobs from your chat platform like Slack.""" def __init__(self, bot): self.jenkins = Jenkins(JENKINS_URL, JENKINS_USERNAME, JENKINS_PASSWORD) super().__init__(bot) @botcmd(split_args_with=None) def jenkins_build(self, msg, args): """Build the job specified by jobName. You can add params!""" # Params are passed like "key1=value1 key2=value2" params = {} try: for arg in args[1:]: params[arg.split('=', 1)[0]] = arg.split('=', 1)[1] except IndexError: return "I don't like that params! Try with this format: key1=value1 key2=value2..." try: self.jenkins.build_job(args[0].strip(), params) except NotFoundException: return "Sorry, I can't find the job. Typo maybe?" return ' '.join([ "The job", args[0].strip(), "has been sent to the queue to be built." ]) @botcmd def jenkins_cancel(self, msg, args): """Cancel a job in the queue by jobId.""" try: self.jenkins.cancel_queue(args.strip()) except NotFoundException: return "Sorry, I can't find the job. Maybe the ID does not exist." return "Job canceled from the queue." @botcmd def jenkins_list(self, msg, args): """List Jenkins jobs. You can filter with strings.""" self.send(msg.to, "I'm getting the jobs list from Jenkins...") search_term = args.strip().lower() jobs = [ job for job in self.jenkins.get_jobs() if search_term.lower() in job['name'].lower() ] return self.format_jobs(jobs) @botcmd def jenkins_describe(self, msg, args): """Describe the job specified by jobName.""" try: job = self.jenkins.get_job_info(args.strip()) except NotFoundException: return "Sorry, I can't find the job. Typo maybe?" return ''.join([ 'Name: ', job['name'], '\n', 'URL: ', job['url'], '\n', 'Description: ', 'None' if job['description'] is None else job['description'], '\n', 'Next Build Number: ', str('None' if job['nextBuildNumber'] is None else job['nextBuildNumber']), '\n', 'Last Successful Build Number: ', str('None' if job['lastBuild'] is None else job['lastBuild'] ['number']), '\n', 'Last Successful Build URL: ', 'None' if job['lastBuild'] is None else job['lastBuild']['url'], '\n' ]) @botcmd def jenkins_running(self, msg, args): """List running jobs.""" self.send(msg.to, "I will ask for the current running builds list!") jobs = self.jenkins.get_running_builds() return self.format_running_jobs(jobs) @botcmd(split_args_with=None) def jenkins_stop(self, msg, args): """Stop the building job specified by jobName and jobNumber.""" try: int(args[1].strip()) except ValueError: return "You have to specify the jobNumber: \"!jenkins stop <jobName> <jobNumber>" try: self.jenkins.stop_build(args[0].strip(), int(args[1].strip())) except NotFoundException: return "Sorry, I can't find the job. Typo maybe?" return ' '.join(["The job", args[0].strip(), "has been stopped."]) @botcmd def jenkins_queue(self, msg, args): """List jobs in queue.""" self.send(msg.to, "Getting the job queue...") jobs = self.jenkins.get_queue_info() return self.format_queue_jobs(jobs) def format_jobs(self, jobs): """Format jobs list""" if len(jobs) == 0: return "I haven't found any job." max_length = max([len(job['name']) for job in jobs]) return '\n'.join([ '%s (%s)' % (job['name'].ljust(max_length), job['url']) for job in jobs ]).strip() def format_queue_jobs(self, jobs): """Format queue jobs list""" if len(jobs) == 0: return "It seems that there is not jobs in queue." return '\n'.join([ '%s - %s (%s)' % (str(job['id']), job['task']['name'], job['task']['url']) for job in jobs ]).strip() def format_running_jobs(self, jobs): """Format running jobs list""" if len(jobs) == 0: return "There is no running jobs!" return '\n'.join([ '%s - %s (%s) - %s' % (str(job['number']), job['name'], job['url'], job['executor']) for job in jobs ]).strip()
class JenkinsNotifier(BotPlugin): """JenkinsBot is an Err plugin to manage Jenkins CI jobs from your chat platform like Slack.""" status = {'blue':'SUCCESS', 'blue_anime':'IN PROGRESS','red': 'FAILED', 'red_anime': 'IN PROGRESS', 'disabled': 'DISABLED', 'aborted':'ABORTED', 'notbuilt': 'NOTBUILT', 'yellow': 'UNSTABLE'} failedjobsstring = " " def __init__(self, bot, name): self.jenkins = Jenkins(JENKINS_URL, JENKINS_USERNAME, JENKINS_TOKEN) super().__init__(bot, name) @botcmd(split_args_with=None) def jn_build(self, msg, args): """Build the job specified by jobName. You can add params!""" # Params are passed like "key1=value1 key2=value2" params = {} #try: # for arg in args[1:]: # params[arg.split('=', 1)[0]] = arg.split('=', 1)[1] #except IndexError: # return "I don'G like that params! Try with this format: key1=value1 key2=value2..." jobName = ''.join([args[0],' ',args[1]]) #TODO handle jobname with spaces in it, space is cosidered argument splitter try: self.jenkins.build_job(jobName, params) except NotFoundException: return ' '.join(["Sorry, I can't find the job. Typo maybe?"," ARGS=",jobName]) return ' '.join(["The job", args[0].strip(), "has been sent to the queue to be built."]) @botcmd def jn_cancel(self, msg, args): """Cancel a job in the queue by jobId.""" try: self.jenkins.cancel_queue(args.strip()) except NotFoundException: return "Sorry, I can't find the job. Maybe the ID does not exist." return "Job canceled from the queue." @botcmd def jn_list(self, msg, args): """List Jenkins jobs. You can filter with strings.""" self.send(msg.to, "I'm getting the jobs list from Jenkins...") search_term = args.strip().lower() jobs = [job for job in self.jenkins.get_jobs() if search_term.lower() in job['name'].lower()] return self.format_jobs(jobs) @botcmd def jn_status(self, msg, args): """List Jenkins jobs with their current status.""" self.send(msg.to, "I'm getting the jobs with status Jenkins...") search_term = args.strip().lower() jobs = [job for job in self.jenkins.get_jobs() if search_term.lower() in job['fullname'].lower()] return self.format_job_status(jobs) @botcmd def jn_describe(self, msg, args): """Describe the job specified by jobName.""" try: job = self.jenkins.get_job_info(args.strip()) except NotFoundException: return "Sorry, I can't find the job. Typo maybe?" return ''.join([ 'Name: ', job['name'], '\n', 'URL: ', job['url'], '\n', 'Description: ', 'None' if job['description'] is None else job['description'], '\n', 'Next Build Number: ', str('None' if job['nextBuildNumber'] is None else job['nextBuildNumber']), '\n', 'Last Successful Build Number: ', str('None' if job['lastBuild'] is None else job['lastBuild']['number']), '\n', 'Last Successful Build URL: ', 'None' if job['lastBuild'] is None else job['lastBuild']['url'], '\n' ]) @botcmd def jn_running(self, msg, args): """List running jobs.""" self.send(msg.to, "I will ask for the current running builds list!") jobs = self.jenkins.get_running_builds() return self.format_running_jobs(jobs) @botcmd(split_args_with=None) def jn_stop(self, msg, args): """Stop the building job specified by jobName and jobNumber.""" try: int(args[1].strip()) except ValueError: return "You have to specify the jobNumber: \"!jenkins stop <jobName> <jobNumber>" try: self.jenkins.stop_build(args[0].strip(), int(args[1].strip())) except NotFoundException: return "Sorry, I can't find the job. Typo maybe?" return ' '.join(["The job", args[0].strip(), "has been stopped."]) @botcmd def jn_queue(self, msg, args): """List jobs in queue.""" self.send(msg.to, "Getting the job queue...") jobs = self.jenkins.get_queue_info() return self.format_queue_jobs(jobs) @botcmd def jn_msgtimer(self, msg, args): """Sends messages at fix intervals.""" yield "Starting timer" self.start_poller(5, self.my_callback) self.send(msg.to, "Boo! Bet you weren't expecting me, were you?", ) def my_callback(self): self.log.info('I am called every 5sec') self.send(self.build_identifier("#errbottestchannel"), "I am called every 5sec", ) @botcmd def jn_failed(self, msg, args): """List Jenkins jobs with failed status.""" self.send(msg.to, "I'm getting the failed jobs ...") failedJobs = [] search_term = args.strip().lower() jobs = [job for job in self.jenkins.get_jobs() if search_term.lower() in job['fullname'].lower()] for job in jobs: if self.status[job['color']] == 'FAILED': failedJobs.append(job) return self.format_job_status(failedJobs) # SUPPORT FUNCTIONS START HERE def format_jobs(self, jobs): """Format jobs list""" if len(jobs) == 0: return "I haven't found any job." max_length = max([len(job['name']) for job in jobs]) return '\n'.join(['%s (%s)' % (job['name'].ljust(max_length), job['url']) for job in jobs]).strip() def format_queue_jobs(self, jobs): """Format queue jobs list""" if len(jobs) == 0: return "It seems that there is not jobs in queue." return '\n'.join(['%s - %s (%s)' % (str(job['id']), job['task']['name'], job['task']['url']) for job in jobs]).strip() def format_running_jobs(self, jobs): """Format running jobs list""" if len(jobs) == 0: return "There is no running jobs!" return '\n'.join(['%s - %s (%s) - %s' % (str(job['number']), job['name'], job['url'], job['executor']) for job in jobs]).strip() @botcmd def jn_queue(self, msg, args): """List jobs in queue.""" self.send(msg.to, "Getting the job queue...") jobs = self.jenkins.get_queue_info() return self.format_queue_jobs(jobs) def format_jobs(self, jobs): """Format jobs list""" if len(jobs) == 0: return "I haven't found any job." max_length = max([len(job['name']) for job in jobs]) return '\n'.join(['%s (%s)' % (job['name'].ljust(max_length), job['url']) for job in jobs]).strip() def format_queue_jobs(self, jobs): """Format queue jobs list""" if len(jobs) == 0: return "It seems that there is not jobs in queue." return '\n'.join(['%s - %s (%s)' % (str(job['id']), job['task']['name'], job['task']['url']) for job in jobs]).strip() def format_running_jobs(self, jobs): """Format running jobs list""" if len(jobs) == 0: return "There is no running jobs!" return '\n'.join(['%s - %s (%s) - %s' % (str(job['number']), job['name'], job['url'], job['executor']) for job in jobs]).strip() def format_job_status(self, jobs): """Format job status""" if len(jobs) == 0: return "there are no jobs to return" return '\n'.join(['%s (%s)' % (job['fullname'], self.status[job['color']]) for job in jobs]).strip()
def main(): module_args = { 'api': { 'type': 'str', 'required': False }, 'wait_for': { 'type': 'str', 'required': False, }, } module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) result = { 'changed' :False, } # format: <github-user>:<github-token> auth_file = path.join(Path.home(), ".jenkins/auth") with open(auth_file) as af: auth = [line.strip() for line in af if not line.strip().startswith("#")] github_user, github_token = auth[0].split(":") # jenkins server name instance = environ['INSTANCE'] workspace = environ['WORKSPACE'] domain = environ['DOMAIN'] server_name = f'https://jenkins-{instance}.{workspace}.{domain}' server = Jenkins(server_name, username=github_user, password=github_token, timeout=10) if module.params['api']: info, _ = inspect_server(server, result) if module.params['api'] == "/quietDown": if info['quietingDown'] == True: module.exit_json(**result) # Already quiesced if not module.check_mode: server.quiet_down() result['changed'] = True module.exit_json(**result) elif module.params['wait_for']: if module.params['wait_for'] == 'normal_operation': info = {} try: info, _ = inspect_server(server, result) except: pass if 'mode' in info and info['mode'] == "NORMAL": serverAPI = jenkinsapi.jenkins.Jenkins(server_name, username=github_user, password=github_token, timeout=10, useCrumb=True) if serverAPI.is_quieting_down: serverAPI.cancel_quiet_down() result['changed'] = True module.exit_json(**result) # Already operating normally if server.wait_for_normal_op(NORMAL_TIMEOUT): result['changed'] = True module.exit_json(**result) # Successfully waited else: # Waiting failed module.fail_json(msg=f'Timeout waiting for server transition to NORMAL operation mode ({NORMAL_TIMEOUT}s)', **result) elif module.params['wait_for'] == 'running_jobs': info, _ = inspect_server(server, result) running_builds = server.get_running_builds() if len(running_builds) == 0: module.exit_json(**result) # No jobs running else: result['changed'] = True if not module.check_mode: _start = time() while time() < _start + RUNNING_JOBS_TIMEOUT: running_builds = server.get_running_builds() if len(running_builds) == 0: module.exit_json(**result) # Successfully waited for running jobs to end # Jobs still running, fail module.fail_json(msg=f'Timeout waiting for running jobs to complete ({RUNNING_JOBS_TIMEOUT}s)', **result)