Skip to content

Instantly share code, notes, and snippets.

@zodman
Last active September 18, 2018 22:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zodman/529c6b01cc070d1e5337440adc61c1be to your computer and use it in GitHub Desktop.
Save zodman/529c6b01cc070d1e5337440adc61c1be to your computer and use it in GitHub Desktop.
rundeck tail log and project status
#!/usr/bin/env python
# coding: utf-8
from __future__ import unicode_literals
from __future__ import print_function
import logging
import os
import requests
import colorama
try:
# Python 2
from urlparse import urljoin
except ModuleNotFoundError:
# Python 3
from urllib.parse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning
colorama.init()
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class Rundeck():
def __init__(self, rundeck_url, token=None, username=None, password=None,
api_version=18, verify=True):
self.rundeck_url = rundeck_url
self.API_URL = urljoin(rundeck_url, '/api/{}'.format(api_version))
self.token = token
self.username = username
self.password = password
self.verify = verify
self.auth_cookie = self.auth()
def auth(self):
url = urljoin(self.rundeck_url, '/j_security_check')
p = {'j_username': self.username, 'j_password': self.password}
r = requests.post(
url,
params=p,
verify=self.verify,
# Disable redirects, otherwise we get redirected twice and need to
# return r.history[0].cookies['JSESSIONID']
allow_redirects=False)
return r.cookies['JSESSIONID']
def __request(self, method, url, params=None):
#logger.info('{} {} Params: {}'.format(method, url, params))
cookies = {'JSESSIONID': self.auth_cookie}
h = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Rundeck-Auth-Token': self.token
}
r = requests.request(
method, url, cookies=cookies, headers=h, json=params,
verify=self.verify
)
logger.debug(r.content)
r.raise_for_status()
try:
return r.json()
except ValueError as e:
logger.error(e.message)
return r.content
def __get(self, url, params=None):
return self.__request('GET', url, params)
def __post(self, url, params=None):
return self.__request('POST', url, params)
def __delete(self, url, params=None):
return self.__request('DELETE', url, params)
def list_tokens(self, user=None):
url = '{}/tokens'.format(self.API_URL)
if user:
url += '/{}'.format(user)
return self.__get(url)
def get_token(self, token_id):
url = '{}/token/{}'.format(self.API_URL, token_id)
return self.__get(url)
def create_token(self, user):
url = '{}/tokens/{}'.format(self.API_URL, user)
return self.__post(url)
def delete_token(self, token_id):
url = '{}/token/{}'.format(self.API_URL, token_id)
return self.__delete(url)
def system_info(self):
url = '{}/system/info'.format(self.API_URL)
return self.__get(url)
def set_active_mode(self):
url = '{}/system/executions/enable'.format(self.API_URL)
return self.__post(url)
def set_passive_mode(self):
url = '{}/system/executions/disable'.format(self.API_URL)
return self.__post(url)
def list_system_acl_policies(self):
url = '{}/system/acl/'.format(self.API_URL)
return self.__get(url)
def get_acl_policy(self, policy):
url = '{}/system/acl/{}'.format(self.API_URL, policy)
return self.__get(url)
def list_projects(self):
url = '{}/projects'.format(self.API_URL)
return self.__get(url)
def list_jobs(self, project):
url = '{}/project/{}/jobs'.format(self.API_URL, project)
return self.__get(url)
def list_all_jobs(self):
jobs = []
for p in self.list_projects():
jobs += self.list_jobs(p['name'])
return jobs
def get_job(self, name, project=None):
if project:
jobs = self.list_jobs(project)
else:
jobs = []
for p in self.list_projects():
jobs += self.list_jobs(p['name'])
return next(job for job in jobs if job['name'] == name)
def run_job(self, job_id, args=None, options=None, log_level=None,
as_user=None, node_filter=None):
url = '{}/job/{}/run'.format(self.API_URL, job_id)
params = {
'logLevel': log_level,
'asUser': as_user,
'filter': node_filter
}
if options is None:
params["argString"] = args
else:
params["options"] = options
return self.__post(url, params=params)
def run_job_by_name(self, name, *args, **kwargs):
job = self.get_job(name)
return self.run_job(job['id'], *args, **kwargs)
def get_executions_for_job(self, job_id=None, job_name=None):
# http://rundeck.org/docs/api/#getting-executions-for-a-job
if not job_id:
if not job_name:
raise RuntimeError("Either job_name or job_id is required")
job_id = self.get_job(job_name).get('id')
url = '{}/job/{}/executions'.format(self.API_URL, job_id)
return self.__get(url)
def query_executions(self, project, name=None, group=None, status=None,
user=None, recent=None, adhoc=None):
# http://rundeck.org/docs/api/#execution-query
url = '{}/project/{}/executions'.format(self.API_URL, project)
params = {
'jobListFilter': name,
'userFilter': user,
'groupPath': group,
'statusFilter': status,
'adhoc': adhoc,
'recentFilter': recent
}
return self.__get(url, params=params)
def list_running_executions(self, project):
url = '{}/project/{}/executions/running'.format(self.API_URL, project)
return self.__get(url)
def execution_state(self, exec_id):
url = '{}/execution/{}/state'.format(self.API_URL, exec_id)
return self.__get(url)
def list_jobs_by_group(self, project, groupPath=None):
url = '{}/project/{}/jobs'.format(self.API_URL, project)
params = {'groupPath': groupPath}
return self.__post(url, params=params)
def execution_output_by_id(self, exec_id):
url = '{}/execution/{}/output?lastlines=20'.format(self.API_URL, exec_id)
return self.__get(url)
def execution_info_by_id(self, exec_id):
url = '{}/execution/{}'.format(self.API_URL, exec_id)
return self.__get(url)
def abort_execution(self, exec_id):
url = '{}/execution/{}/abort'.format(self.API_URL, exec_id)
return self.__get(url)
if __name__ == '__main__':
from pprint import pprint
rundeck_url = os.environ.get('RUNDECK_URL', "https://rundeck.interalia.net")
username = os.environ.get('RUNDECK_USER', "admin")
password = os.environ.get('RUNDECK_PASS', None)
if not password:
import getpass
#import tempfile
#import os
#tempdir = tempfile.gettempdir()
#filepass = os.path.join(tempdir, ".rundeckpass")
password = getpass.getpass()
assert rundeck_url, 'Rundeck URL is required'
assert username, 'Username is required'
assert password, 'Password is required'
rd = Rundeck(
rundeck_url, username=username, password=password,
verify=False
)
project = os.environ.get("RUNDECK_PROJECT", "*")
execs = rd.list_running_executions(project)
if not execs:
print("No deploy executions")
for i in execs.get("executions"):
print( colorama.Fore.GREEN + "{}/{} [{}]" .format(i.get("project"), i.get("job").get("name"), colorama.Style.DIM + colorama.Back.GREEN+ i.get("status") + colorama.Style.RESET_ALL ))
print(colorama.Style.RESET_ALL)
print("#### {}#ouput".format(i.get("permalink")))
print(colorama.Style.RESET_ALL)
exec_id = i.get("id")
output = rd.execution_output_by_id(exec_id)
if output.get("entries"):
print("------ LOG -----")
print(colorama.Style.RESET_ALL)
for log in output.get("entries"):
print(log.get("log"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment