def run(self): url = 'https://api.travis-ci.org/repos/{repo}/builds'.format(repo=self.repo) request_handler = RequestHandler() req = request_handler.get(url) if log.isEnabledFor(logging.DEBUG): log.debug("\n%s", jsonpp(req.content)) try: self.parse_results(req.content) except (KeyError, ValueError): exception = traceback.format_exc().split('\n')[-2] # this covers up the traceback info and makes it harder to debug #raise UnknownError('failed to parse expected json response from Travis CI API: {0}'.format(exception)) qquit('UNKNOWN', 'failed to parse expected json response from Travis CI API: {0}. {1}'. format(exception, support_msg_api()))
def __init__(self): super(ConsulPeerCount, self).__init__() self.name = 'Consul' self.default_port = 8500 self.host = None self.port = None self.request_handler = RequestHandler() self.msg = 'NO MESSAGE DEFINED'
def __init__(self): # Python 2.x super(TravisDebugSession, self).__init__() # Python 3.x # super().__init__() self.timeout_default = 600 self.verbose_default = 2 self.job_id = None self.travis_token = None self.repo = None self.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Travis-API-Version': '3', 'User-Agent': prog } self.request_handler = RequestHandler()
def test_request_handler_failure(): try: RequestHandler().get('127.0.0.1:1') raise AssertionError( 'failed to raise exception for RequestHandler.get(127.0.0.1:1)' ) except CriticalError: pass
class ConsulKeyCheck(KeyCheckNagiosPlugin): def __init__(self): super(ConsulKeyCheck, self).__init__() self.name = "Consul" self.default_port = 8500 self.request_handler = RequestHandler() def extract_value(self, content): # pylint: disable=no-self-use json_data = None try: json_data = json.loads(content) except ValueError: raise UnknownError("non-json data returned by consul: '%s'. %s" % (content, support_msg_api())) value = None if not isList(json_data): raise UnknownError("non-list returned by consul: '%s'. %s" % (content, support_msg_api())) if not json_data: raise UnknownError("blank list returned by consul! '%s'. %s" % (content, support_msg_api())) if len(json_data) > 1: raise UnknownError( "more than one key returned by consul! response = '%s'. %s" % (content, support_msg_api()) ) try: value = json_data[0]["Value"] except KeyError: raise UnknownError( "couldn't find field 'Value' in response from consul: '%s'. %s" % (content, support_msg_api()) ) try: value = base64.decodestring(value) except TypeError as _: raise UnknownError( "invalid data returned for key '{0}' value = '{1}', failed to base64 decode".format(self.key, value) ) return value # closure factory @staticmethod def check_response_code(msg): def tmp(req): if req.status_code != 200: err = "" if req.content and isStr(req.content) and len(req.content.split("\n")) < 2: err += ": " + req.content raise CriticalError("{0}: '{1}' {2}{3}".format(msg, req.status_code, req.reason, err)) return tmp def read(self): req = None # could use ?raw to get the value without base64 but leaving base64 encoding as it's safer url = "http://%(host)s:%(port)s/v1/kv/%(key)s" % self.__dict__ self.request_handler.check_response_code = self.check_response_code( "failed to retrieve Consul key '{0}'".format(self.key) ) req = self.request_handler.get(url) value = self.extract_value(req.content) return value
def run_host(self, host, url): log.info('querying %s', host) req = RequestHandler().get(url) json_data = json.loads(req.text) beans = json_data['beans'] for bean in beans: log.debug('processing Regions bean') if bean['name'] == 'Hadoop:service=HBase,name=RegionServer,sub=Regions': self.process_bean(host, bean)
def __init__(self): # Python 2.x super(CheckDockerhubRepoBuildStatus, self).__init__() # Python 3.x # super().__init__() self.request = RequestHandler() self.statuses = { '0': 'Queued', '3': 'Building', '10': 'Success', '-1': 'Error', '-4': 'Cancelled', } self.msg = 'DockerHub repo ' self.repo = None self.tag = None self.max_pages = None self.start_time = None self.ok()
class ConsulKeyCheck(KeyCheckNagiosPlugin): def __init__(self): super(ConsulKeyCheck, self).__init__() self.name = 'Consul' self.default_port = 8500 self.request_handler = RequestHandler() def extract_value(self, content): json_data = None try: json_data = json.loads(content) except ValueError: raise UnknownError("non-json data returned by consul: '%s'. %s" % (content, support_msg_api())) value = None if not isList(json_data): raise UnknownError("non-list returned by consul: '%s'. %s" % (content, support_msg_api())) if not json_data: raise UnknownError("blank list returned by consul! '%s'. %s" % (content, support_msg_api())) if len(json_data) > 1: raise UnknownError("more than one key returned by consul! response = '%s'. %s" \ % (content, support_msg_api())) try: value = json_data[0]['Value'] except KeyError: raise UnknownError("couldn't find field 'Value' in response from consul: '%s'. %s" % (content, support_msg_api())) try: # decodestring might be deprecated but decodebytes isn't available on Python 2.7 #value = base64.decodebytes(value) value = base64.decodestring(value) except TypeError: raise UnknownError("invalid data returned for key '{0}' value = '{1}', failed to base64 decode" .format(self.key, value)) return value # closure factory @staticmethod def check_response_code(msg): def tmp(req): if req.status_code != 200: err = '' if req.content and isStr(req.content) and len(req.content.split('\n')) < 2: err += ': ' + req.content raise CriticalError("{0}: '{1}' {2}{3}".format(msg, req.status_code, req.reason, err)) return tmp def read(self): # could use ?raw to get the value without base64 but leaving base64 encoding as it's safer url = 'http://%(host)s:%(port)s/v1/kv/%(key)s' % self.__dict__ self.request_handler.check_response_code = \ self.check_response_code("failed to retrieve Consul key '{0}'".format(self.key)) req = self.request_handler.get(url) value = self.extract_value(req.content) return value
def __init__(self): # Python 2.x super(TravisLastBuildLog, self).__init__() # Python 3.x # super().__init__() self.timeout_default = 600 self.verbose_default = 1 self.travis_token = None self.repo = None self.job_id = None self.num = None self.completed = False self.failed = False self.plaintext = False self.color = False self.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Travis-API-Version': '3', 'User-Agent': prog } self.request_handler = RequestHandler()
def __init__(self): # Python 2.x super(RestNagiosPlugin, self).__init__() # Python 3.x # super().__init__() self.name = None self.default_host = 'localhost' self.default_port = 80 self.default_user = None self.default_password = None self.host = None self.port = None self.user = None self.password = None self.protocol = 'http' self.msg = 'rest msg not defined yet' self.request = RequestHandler() self.req = None self.json_data = None self.path = None self.json = False self.auth = True self.ok()
def run_host(self, host, url): req = RequestHandler().get(url) json_data = json.loads(req.text) uptime = None beans = json_data['beans'] if self.since_uptime: for bean in beans: if bean['name'] == 'java.lang:type=Runtime': uptime = int(bean['Uptime'] / 1000) break if not uptime: raise UnknownError("failed to find uptime in JMX stats for host '{}'. {}"\ .format(host, support_msg_api())) for bean in beans: if bean['name'] == 'Hadoop:service=HBase,name=RegionServer,sub=Regions': self.process_bean(host, bean, uptime)
def print_region_stats(self, host): req = RequestHandler().get('http://{host}:{port}/jmx'.format(host=host, port=self.port)) json_data = json.loads(req.text) uptime = None beans = json_data['beans'] for bean in beans: if bean['name'] == 'java.lang:type=Runtime': uptime = int(bean['Uptime'] / 1000) break if not uptime: raise UnknownError("failed to find uptime in JMX stats for host '{}'. {}".format(host, support_msg_api())) region_regex = re.compile('^Namespace_{namespace}_table_{table}_region_(.+)_metric_(.+)RequestCount'\ .format(namespace=self.namespace, table=self.table)) for bean in beans: if bean['name'] == 'Hadoop:service=HBase,name=RegionServer,sub=Regions': for key in sorted(bean): match = region_regex.match(key) if match: region = match.group(1) metric = match.group(2) print('{:20s}\t{:20s}\t\t{:10s}\t{:.1f}'.format(host, region, metric, bean[key] / uptime))
def __init__(self): # Python 2.x super(RestNagiosPlugin, self).__init__() # Python 3.x # super().__init__() self.name = None self.default_host = 'localhost' self.default_port = 80 self.default_user = None self.default_password = None self.host = None self.port = None self.user = None self.password = None self.protocol = 'http' self.msg = 'rest msg not defined yet' self.request = RequestHandler() self.req = None self.json_data = None self.path = None self.json = False self.ok()
def get_status(self): if self.get_opt('ssl'): self.protocol = 'https' url = '%(protocol)s://%(host)s:%(port)s/api/atlas/admin/status' % self.__dict__ req = RequestHandler().get(url) return self.parse(req)
def get_status(self): url = 'http://{0}:{1}/status'.format(self.host, self.port) req = RequestHandler().get(url) status = self.parse(req) return status
class RestNagiosPlugin(NagiosPlugin): __version__ = __version__ # abstract class __metaclass__ = ABCMeta def __init__(self): # Python 2.x super(RestNagiosPlugin, self).__init__() # Python 3.x # super().__init__() self.name = None self.default_host = 'localhost' self.default_port = 80 self.default_user = None self.default_password = None self.host = None self.port = None self.user = None self.password = None self.protocol = 'http' self.msg = 'rest msg not defined yet' self.request = RequestHandler() self.request_method = 'get' self.req = None self.json_data = None self.path = None self.json = False self.headers = {} self.auth = True self.ok() def add_options(self): self.add_hostoption(name=self.name, default_host=self.default_host, default_port=self.default_port) if self.auth: self.add_useroption(name=self.name, default_user=self.default_user, default_password=self.default_password) self.add_ssl_option() def process_options(self): self.no_args() self.host = self.get_opt('host') self.port = self.get_opt('port') validate_host(self.host) validate_port(self.port) if self.auth: self.user = self.get_opt('user') self.password = self.get_opt('password') if self.auth == 'optional': if self.user and self.password: validate_user(self.user) validate_password(self.password) else: validate_user(self.user) validate_password(self.password) ssl = self.get_opt('ssl') log_option('ssl', ssl) if ssl and self.protocol == 'http': self.protocol = 'https' if self.json: # recommended for many systems like CouchDB self.headers['Accept'] = 'application/json' def run(self): start_time = time.time() self.req = self.query() query_time = time.time() - start_time if self.json: log.info('parsing json response') self.process_json(self.req.content) else: log.info('parsing response') self.parse(self.req) if '|' not in self.msg: self.msg += ' |' if ' query_time=' not in self.msg: self.msg += ' query_time={0:.4f}s'.format(query_time) def query(self): url = '{proto}://{host}:{port}/'.format(proto=self.protocol, host=self.host, port=self.port) if self.path: url += self.path.lstrip('/') auth = None if self.user and self.password: log.info('authenticating to Rest API') auth = (self.user, self.password) req = self.request.req(self.request_method, url, auth=auth, headers=self.headers) return req #@abstractmethod def parse(self, req): pass #@abstractmethod def parse_json(self, json_data): pass def process_json(self, content): try: self.json_data = json.loads(content) if log.isEnabledFor(logging.DEBUG): log.debug('JSON prettified:\n\n%s\n%s', jsonpp(self.json_data), '='*80) return self.parse_json(self.json_data) #except (KeyError, ValueError) as _: #raise UnknownError('{0}: {1}. {2}'.format(type(_).__name__, _, support_msg_api())) except (KeyError, ValueError): raise UnknownError('{0}. {1}'.format(self.exception_msg(), support_msg_api()))
class TravisLastBuildLog(CLI): def __init__(self): # Python 2.x super(TravisLastBuildLog, self).__init__() # Python 3.x # super().__init__() self.timeout_default = 600 self.verbose_default = 1 self.travis_token = None self.repo = None self.job_id = None self.num = None self.completed = False self.failed = False self.plaintext = False self.color = False self.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Travis-API-Version': '3', 'User-Agent': prog } self.request_handler = RequestHandler() def add_options(self): self.add_opt( '-R', '--repo', default=os.getenv('TRAVIS_REPO'), help='Travis CI repo to find last failed build ($TRAVIS_REPO)') self.add_opt( '-J', '--job-id', default=os.getenv('TRAVIS_JOB_ID'), help='Job ID to download log for a specific job ($TRAVIS_JOB_ID)') self.add_opt( '-T', '--travis-token', default=os.getenv('TRAVIS_TOKEN'), help= 'Travis token required to authenticate to the API ($TRAVIS_TOKEN)') self.add_opt('-n', '--num', default=1, help='Number of builds to pull logs from (default: 1)') self.add_opt('-c', '--completed', action='store_true', default=False, help='Only completed build(s)') self.add_opt('-f', '--failed', action='store_true', default=False, help='Only failed build(s)') #self.add_opt('-A', '--plaintext', action='store_true', default=False, # help='Print in plaintext without fancy shell escapes ' + \ # '(will do this by default if the output is not an interactive terminal ' + \ # 'such as piping through more)') #self.add_opt('-C', '--color', action='store_true', default=False, # help='Force retention of fancy colour output regardless of interactive terminal or not ' + \ # '(for piping through less -R)') def process_options(self): self.travis_token = self.get_opt('travis_token') self.repo = self.get_opt('repo') self.job_id = self.get_opt('job_id') if self.args: if '/' in self.args[0] and '://' not in self.args[0]: if not self.repo: log.info('using argument as --repo') self.repo = self.args[0] elif not self.job_id: log.info('using argument as --job-id') self.job_id = self.args[0] if self.job_id: # convenience to be able to lazily paste a URL like the following and still have it extract the job_id # https://travis-ci.org/HariSekhon/nagios-plugins/jobs/283840596#L1079 self.job_id = self.job_id.split('/')[-1].split('#')[0] validate_chars(self.job_id, 'job id', '0-9') elif self.repo: travis_user = os.getenv('TRAVIS_USER') if '/' not in self.repo: self.repo = '/' + self.repo if self.repo[0] == '/' and travis_user: self.repo = travis_user + self.repo validate_chars(self.repo, 'repo', r'\/\w\.-') else: self.usage('--job-id / --repo not specified') validate_alnum(self.travis_token, 'travis token') self.headers['Authorization'] = 'token {0}'.format(self.travis_token) self.num = self.get_opt('num') validate_int(self.num, 'num', 1) self.num = int(self.num) self.completed = self.get_opt('completed') self.failed = self.get_opt('failed') #self.plaintext = self.get_opt('plaintext') #self.color = self.get_opt('color') #if self.plaintext and self.color: # self.usage('cannot specify --plaintext and --color at the same time, they are mutually exclusive!') # test for interactive, switch off color if piping stdout somewhere #if not self.color and not (sys.__stdin__.isatty() and sys.__stdout__.isatty()): # self.plaintext = True def run(self): if self.job_id: self.print_log(job_id=self.job_id) else: builds = self.get_builds() for build in builds: self.print_log(build=build) @staticmethod def parse_travis_error(req): error_message = '' try: _ = json.loads(req.content) error_message = _['error_message'] except ValueError: if isStr(req.content) and len(req.content.split('\n')) == 1: error_message = req.content return error_message def get_builds(self): builds = self.get_latest_builds() try: builds = self.parse_builds(builds) except (KeyError, ValueError): exception = traceback.format_exc().split('\n')[-2] # this covers up the traceback info and makes it harder to debug #raise UnknownError('failed to parse expected json response from Travis CI API: {0}'.format(exception)) qquit( 'UNKNOWN', 'failed to parse expected json response from Travis CI API: {0}. {1}' .format(exception, support_msg_api())) return builds def get_latest_builds(self): log.info('getting latest builds') # gets 404 unless replacing the slash url = 'https://api.travis-ci.org/repo/{repo}/builds'.format( repo=self.repo.replace('/', '%2F')) # request returns blank without authorization header req = self.request_handler.get(url, headers=self.headers) if log.isEnabledFor(logging.DEBUG): log.debug("\n%s", jsonpp(req.content)) if not isJson(req.content): raise UnknownError('non-json returned by Travis CI. {0}'.format( support_msg_api())) return req.content def parse_builds(self, content): log.debug('parsing build info') build = None collected_builds = [] json_data = json.loads(content) if not json_data or \ 'builds' not in json_data or \ not json_data['builds']: qquit( 'UNKNOWN', "no Travis CI builds returned by the Travis API." + " Either the specified repo '{0}' doesn't exist".format( self.repo) + " or no builds have happened yet?" + " Also remember the repo is case sensitive, for example 'harisekhon/nagios-plugins' returns this" + " blank build set whereas 'HariSekhon/nagios-plugins' succeeds" + " in returning latest builds information") builds = json_data['builds'] # get latest finished failed build last_build_number = None found_newer_passing_build = False for _ in builds: # API returns most recent build first # extra check to make sure we're getting the very latest build number and API hasn't changed build_number = _['number'] if not isInt(build_number): raise UnknownError('build number returned is not an integer!') build_number = int(build_number) if last_build_number is None: last_build_number = int(build_number) + 1 if build_number >= last_build_number: raise UnknownError('build number returned is out of sequence, cannot be >= last build returned' + \ '{0}'.format(support_msg_api())) last_build_number = build_number if self.completed: if len(collected_builds) < self.num and _['state'] in ( 'passed', 'finished', 'failed', 'errored'): collected_builds.append(_) elif self.failed: if _['state'] == 'passed': if not collected_builds and not found_newer_passing_build: log.warning("found more recent successful build #%s with state = '%s'" + \ ", you may not need to debug this build any more", _['number'], _['state']) found_newer_passing_build = True elif _['state'] in ('failed', 'errored'): if len(collected_builds) < self.num: collected_builds.append(_) # by continuing to iterate through the rest of the builds we can check # their last_build numbers are descending for extra sanity checking #break elif len(collected_builds) < self.num: collected_builds.append(_) # by continuing to iterate through the rest of the builds we can check # their last_build numbers are descending for extra sanity checking #break if not collected_builds: qquit('UNKNOWN', 'no recent builds found') if log.isEnabledFor(logging.DEBUG): for build in collected_builds: log.debug("build:\n%s", jsonpp(build)) return collected_builds def print_log(self, build=None, job_id=None): if job_id: self.print_job_log(job_id=job_id) log.info('=' * 80) log.info('end of log for job id %s', job_id) log.info('=' * 80 + '\n') else: if not build: code_error( 'no job id passed to print_log(), nor build to determine job from' ) log.info('getting job id for build #%s', build['number']) if 'jobs' not in build: raise UnknownError('no jobs field found in build, {0}'.format( support_msg_api)) for _ in build['jobs']: _id = _['id'] url = 'https://api.travis-ci.org/jobs/{id}'.format(id=_id) req = self.request_handler.get(url) # if this raises ValueError it'll be caught by run handler job_data = json.loads(req.content) if log.isEnabledFor(logging.DEBUG): log.debug("job id %s status:\n%s", _id, jsonpp(job_data)) if self.failed is True: if job_data['state'] == 'finished' and job_data[ 'status'] in (None, 1, '1'): job = job_data else: job = job_data if not job: raise UnknownError('no job found in build {0}'.format( build['number'])) self.print_job_log(job=job) log.info('=' * 80) log.info('end of log for build number #%s job id %s', build['number'], job['id']) log.info('=' * 80 + '\n') def print_job_log(self, job=None, job_id=None): #if (self.color or not self.plaintext) and 'log' in job: if not job and not job_id: code_error('no job data or job id passed to print_job_log()') content = None if job is not None: if 'log' in job and job['log']: content = job['log'] else: job_id = job['id'] if not content: url = 'https://api.travis-ci.org/jobs/{id}/log.txt?deansi=true'.format( id=job_id) req = self.request_handler.get(url) content = req.content content = re.sub(r'\r', '', content) #if self.plaintext: # leaves a few characters behind which are printable #content = re.sub('[^{0}]'.format(string.printable), '', content) # mandatory stripping ANSI control sequences for now as although color coding is nice # Travis has too many other control sequences that mess up my terminal # strip all control sequences content = strip_ansi_escape_codes(content) print(content)
class TravisDebugSession(CLI): def __init__(self): # Python 2.x super(TravisDebugSession, self).__init__() # Python 3.x # super().__init__() self.timeout_default = 600 self.verbose_default = 2 self.job_id = None self.travis_token = None self.repo = None self.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Travis-API-Version': '3', 'User-Agent': prog } self.request_handler = RequestHandler() def check_job_launch_response_code(self, req): if req.status_code == 409: error_message = self.parse_travis_error(req) error_message += " (if you've just retriggered this you can avoid this error " + \ "using the --ignore-running switch)" if self.get_opt('ignore_running'): log.info('job already running (ignoring)') else: log.info('job already running') raise CriticalError('{0} {1}: {2}'.format( req.status_code, req.reason, error_message)) elif req.status_code != 202: error_message = self.parse_travis_error(req) raise CriticalError("{0} {1}: {2}".format(req.status_code, req.reason, error_message)) def add_options(self): self.add_opt( '-J', '--job-id', default=os.getenv('JOB_ID'), help='Travis Job ID to initiate the debug session ($JOB_ID)') self.add_opt( '-T', '--travis-token', default=os.getenv('TRAVIS_TOKEN'), help= 'Travis token required to authenticate to the API ($TRAVIS_TOKEN)') self.add_opt('-R', '--repo', default=os.getenv('TRAVIS_REPO'), help='Travis CI repo to find last failed build and re-execute a job from it ($TRAVIS_REPO)' + \ ', easier alternative to specifying a specific --job-id' + \ ', convenient if working with the same repo over and over and don\'t want to copy the ' + \ 'job id each time (--job-id takes priority if given as it\'s more specific). ' + \ 'Be aware if running this quickly in succession you will get older and older failed ' + \ 'builds as the last one will still be running, will only re-trigger finished failed builds') self.add_opt( '-i', '--ignore-running', action='store_true', help= 'Ignore job already running error (avoids 409 error if you try to restart debug job)' ) def process_options(self): self.job_id = self.get_opt('job_id') self.travis_token = self.get_opt('travis_token') self.repo = self.get_opt('repo') #if travis_token is None: # self.usage('--travis-token option or ' + # '$TRAVIS_TOKEN environment variable required to authenticate to the API') if self.args: # assume arg is a repo in form of HariSekhon/nagios-plugins but do not use url which we are more likely to # have pasted a travis-ci url to a job, see a few lines further down if '/' in self.args[0] and '://' not in self.args[0]: if not self.repo: log.info('using argument as --repo') self.repo = self.args[0] elif not self.job_id: log.info('using argument as --job-id') self.job_id = self.args[0] if self.job_id: # convenience to be able to lazily paste a URL like the following and still have it extract the job_id # https://travis-ci.org/HariSekhon/nagios-plugins/jobs/283840596#L1079 self.job_id = self.job_id.split('/')[-1].split('#')[0] validate_chars(self.job_id, 'job id', '0-9') elif self.repo: validate_chars(self.repo, 'repo', r'\/\w\.-') else: self.usage('--job-id / --repo not specified') validate_alnum(self.travis_token, 'travis token') self.headers['Authorization'] = 'token {0}'.format(self.travis_token) def run(self): if not self.job_id: if self.repo: latest_failed_build = self.get_latest_failed_build() self.job_id = self.get_failing_job_id_from_build( latest_failed_build) else: code_error('--job-id / --repo not specified, caught late') if self.job_id is None: raise UnknownError( 'no job id was found, aborting getting SSH address') self.launch_job() ssh_address = self.get_ssh_address(job_id=self.job_id) log.info('Executing: ssh -- {0}'.format(ssh_address)) sys.stdout.flush() sys.stderr.flush() self.disable_timeout() os.execvp('ssh', ['--', ssh_address]) def launch_job(self): log.info('triggering debug job {job_id}'.format(job_id=self.job_id)) url = 'https://api.travis-ci.org/job/{job_id}/debug'.format( job_id=self.job_id) self.request_handler.check_response_code = self.check_job_launch_response_code self.request_handler.post(url, headers=self.headers) @staticmethod def parse_travis_error(req): error_message = '' try: _ = json.loads(req.content) error_message = _['error_message'] except ValueError: if isStr(req.content) and len(req.content.split('\n')) == 1: error_message = req.content return error_message def get_latest_failed_build(self): log.info('getting latest failed build') # gets 404 unless replacing the slash url = 'https://api.travis-ci.org/repo/{repo}/builds'.format( repo=self.repo.replace('/', '%2F')) # request returns blank without authorization header req = self.request_handler.get(url, headers=self.headers) if log.isEnabledFor(logging.DEBUG): log.debug("\n%s", jsonpp(req.content)) try: latest_build = self.parse_latest_failed_build(req.content) except (KeyError, ValueError): exception = traceback.format_exc().split('\n')[-2] # this covers up the traceback info and makes it harder to debug #raise UnknownError('failed to parse expected json response from Travis CI API: {0}'.format(exception)) qquit( 'UNKNOWN', 'failed to parse expected json response from Travis CI API: {0}. {1}' .format(exception, support_msg_api())) return latest_build def parse_latest_failed_build(self, content): log.debug('parsing latest failed build info') build = None json_data = json.loads(content) if not json_data or \ 'builds' not in json_data or \ not json_data['builds']: qquit( 'UNKNOWN', "no Travis CI builds returned by the Travis API." + " Either the specified repo '{0}' doesn't exist".format( self.repo) + " or no builds have happened yet?" + " Also remember the repo is case sensitive, for example 'harisekhon/nagios-plugins' returns this" + " blank build set whereas 'HariSekhon/nagios-plugins' succeeds" + " in returning latest builds information") builds = json_data['builds'] # get latest finished failed build last_build_number = None found_newer_passing_build = False for _ in builds: # API returns most recent build first so just take the first one that is completed # extra check to make sure we're getting the very latest build number and API hasn't changed build_number = _['number'] if not isInt(build_number): raise UnknownError('build number returned is not an integer!') build_number = int(build_number) if last_build_number is None: last_build_number = int(build_number) + 1 if build_number >= last_build_number: raise UnknownError('build number returned is out of sequence, cannot be >= last build returned' + \ '{0}'.format(support_msg_api())) last_build_number = build_number if _['state'] == 'passed': if build is None and not found_newer_passing_build: log.warning("found more recent successful build #%s with state = '%s'" + \ ", you may not need to debug this build any more", _['number'], _['state']) found_newer_passing_build = True elif _['state'] in ('failed', 'errored'): if build is None: build = _ # by continuing to iterate through the rest of the builds we can check # their last_build numbers are descending for extra sanity checking #break if build is None: qquit('UNKNOWN', 'no recent failed builds found' + \ ', you may need to specify the --job-id explicitly as shown in the Travis CI UI') if log.isEnabledFor(logging.DEBUG): log.debug("latest failed build:\n%s", jsonpp(build)) return build def get_failing_job_id_from_build(self, build): log.info('getting failed job id for build #%s', build['number']) if 'jobs' not in build: raise UnknownError( 'no jobs field found in build, {0}'.format(support_msg_api)) for _ in build['jobs']: _id = _['id'] url = 'https://api.travis-ci.org/jobs/{id}'.format(id=_id) req = self.request_handler.get(url) # if this raises ValueError it'll be caught by run handler job = json.loads(req.content) if log.isEnabledFor(logging.DEBUG): log.debug("job id %s status:\n%s", _id, jsonpp(job)) if job['state'] == 'finished' and job['status'] in (None, 1, '1'): return _id raise UnknownError('no failed job found in build {0}'.format( build['number'])) def get_ssh_address(self, job_id): log.info('getting SSH address from triggered debug build') max_tries = int(self.timeout / 4) for i in range(1, max_tries + 1): log.info('try {0}/{1}: checking job log for ssh address...'.format( i, max_tries)) ssh_address = self.get_ssh_address_attempt(job_id=job_id) if ssh_address: return ssh_address time.sleep(3) if ssh_address is None: raise CriticalError( 'ssh address not found in output from Travis API. {0}'.format( support_msg_api())) def get_ssh_address_attempt(self, job_id): #url = 'https://travis-ci.org/{repo}/jobs/{job_id}'.format(repo=repo, job_id=job_id) url = 'https://api.travis-ci.org/jobs/{job_id}/log.txt?deansi=true'.format( job_id=job_id) log.debug('GET %s' % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: raise CriticalError(_) log.debug("response: %s %s", req.status_code, req.reason) log.debug("content:\n%s\n%s\n%s", '=' * 80, req.content.strip(), '=' * 80) # Travis CI behaviour has changed from 200 with no content indicating build log empty, not started yet # to now returning "500 Internal Server Error", content: "Sorry, we experienced an error." if req.status_code == 500: # don't output 500 it will confuse users in to thinking there is a real error which 500 usually indicates #log.info('500 internal server error, build not started yet') log.info('build not started yet') return None if req.status_code != 200: error_message = self.parse_travis_error(req) raise CriticalError('{0} {1}: {2}'.format(req.status_code, req.reason, error_message)) content = req.content if not content: log.info('build log empty, build not started yet') return None # find last non-blank line - do this after checking for no content otherwise will hit StopIteration last_line = next(_ for _ in reversed(content.split('\n')) if _) #log.debug('last line: %s', last_line) # 'Done: Job Cancelled' if 'Job Cancelled' in last_line: raise CriticalError(last_line) elif 'Your build has been stopped' in last_line: raise CriticalError(last_line) # Done. Your build exited with 0 elif 'build exited with' in last_line: raise CriticalError(last_line) # The build has been terminated elif 'build has been terminated' in last_line: raise CriticalError(last_line) ssh_address = None regex_ssh = re.compile( r'^\s*ssh\s+(\w+\@{host_regex})\s*$'.format(host_regex=host_regex), re.I) for line in content.split('\n'): match = regex_ssh.match(line) if match: ssh_address = match.group(1) break return ssh_address
def __init__(self): super(CheckConsulKey, self).__init__() self.name = 'Consul' self.default_port = 8500 self.request_handler = RequestHandler()
class CheckDockerhubRepoBuildStatus(NagiosPlugin): def __init__(self): # Python 2.x super(CheckDockerhubRepoBuildStatus, self).__init__() # Python 3.x # super().__init__() self.request = RequestHandler() self.statuses = { '0': 'Queued', '3': 'Building', '10': 'Success', '-1': 'Error', '-4': 'Cancelled', } self.msg = 'DockerHub repo ' self.repo = None self.tag = None self.max_pages = None self.start_time = None self.ok() def add_options(self): self.add_opt('-r', '--repo', help="DockerHub repository to check, in form of '<user>/<repo>' eg. harisekhon/pytools") self.add_opt('-T', '--tag', help='Check status of only this tag eg. latest or 2.7') self.add_opt('-p', '--pages', default=1, metavar='num', help='Max number of API pages to iterate on to find the latest build (default: 1)' + \ '. If increasing this you will probably also need to increase --timeout') def process_options(self): self.repo = self.get_opt('repo') validate_chars(self.repo, 'repo', 'A-Za-z0-9/_-') # official repos don't have slashes in them but then you can't check their build statuses either if '/' not in self.repo: self.usage('--repo must contain a slash (/) in it - ' + \ 'official repos are not supported as DockerHub doesn\'t expose their build info') (namespace, repo) = self.repo.split('/', 1) validate_chars(namespace, 'namespace', 'A-Za-z0-9_-') validate_chars(repo, 'repo', 'A-Za-z0-9_-') self.repo = '{0}/{1}'.format(namespace, repo) # not needed as dashes and underscores are all that validation above permits through and they # are returned as is and processed successfully by DockerHub API #(user, repo) = self.repo.split('/', 1) #repo = urllib.quote_plus(repo) #self.repo = '{0}/{1}'.format(user, repo) self.tag = self.get_opt('tag') if self.tag is not None: # if you have a tag which characters other than these then please raise a ticket for extension at: # # https://github.com/harisekhon/nagios-plugins/issues # self.tag = self.tag.lstrip(':') validate_chars(self.tag, 'tag', r'A-Za-z0-9/\._-') #if not self.tag: # self.usage('--tag cannot be blank if given') self.max_pages = self.get_opt('pages') # if you have to iterate more than 20 pages you have problems, and this check will take ages validate_int(self.max_pages, 'max pages', 1, 20) self.max_pages = int(self.max_pages) def run(self): start_time = time.time() for page in range(1, self.max_pages + 1): url = 'https://registry.hub.docker.com/v2/repositories/{repo}/buildhistory?page={page}'\ .format(repo=self.repo, page=page) req = self.request.get(url) if log.isEnabledFor(logging.DEBUG): log.debug(jsonpp(req.content)) json_data = json.loads(req.content) log.debug('%s out of %s results returned for page %s', len(json_data['results']), json_data['count'], page) if self.process_results(json_data): # not quite as accurate as before as it now includes processing time but close enough query_time = time.time() - start_time if '|' not in self.msg: self.msg += ' |' self.msg += ' query_time={0:.2f}s'.format(query_time) return True extra_info = '' if self.verbose: extra_info = ' ({0} page{1} of API output)'\ .format(self.max_pages, plural(self.max_pages)) raise UnknownError('no completed builds found in last {0} builds{1}'.format(self.max_pages * 10, extra_info)) def process_results(self, json_data): for result in json_data['results']: tag = result['dockertag_name'] build_code = result['build_code'] _id = result['id'] # Skip Queued / Building as we're only interested in latest completed build status if int(result['status']) in (0, 3): if log.isEnabledFor(logging.DEBUG): log.debug("skipping queued/in progress build tag '%s', id: %s, build_code: %s", tag, _id, build_code) continue if self.tag and self.tag != tag: if log.isEnabledFor(logging.DEBUG): log.debug("skipping build tag '%s', id: %s, build_code: %s, does not match given --tag %s", tag, _id, build_code, self.tag) continue self.process_result(result) return True return False def process_result(self, result): _id = result['id'] log.info('latest build id: %s', _id) status = result['status'] log.info('status: %s', status) if not isInt(status, allow_negative=True): raise UnknownError('non-integer status returned by DockerHub API. {0}'.format(support_msg_api())) tag = result['dockertag_name'] log.info('tag: %s', tag) trigger = result['cause'] log.info('trigger: %s', trigger) created_date = result['created_date'] log.info('created date: %s', created_date) last_updated = result['last_updated'] log.info('last updated: %s', last_updated) created_datetime = datetime.datetime.strptime(created_date.split('.')[0], '%Y-%m-%dT%H:%M:%S') updated_datetime = datetime.datetime.strptime(last_updated.split('.')[0], '%Y-%m-%dT%H:%M:%S') build_latency_timedelta = updated_datetime - created_datetime build_latency = build_latency_timedelta.total_seconds() log.info('build latency (creation to last updated): %s', build_latency) # results in .0 floats anyway build_latency = int(build_latency) build_code = result['build_code'] build_url = 'https://hub.docker.com/r/{0}/builds/{1}'.format(self.repo, build_code) log.info('latest build URL: %s', build_url) if str(status) in self.statuses: status = self.statuses[str(status)] else: log.warning("status code '%s' not recognized! %s", status, support_msg_api()) log.warning('defaulting to assume status is an Error') status = 'Error' if status != 'Success': self.critical() self.msg += "'{repo}' last completed build status: '{status}', tag: '{tag}', build code: {build_code}"\ .format(repo=self.repo, status=status, tag=tag, build_code=build_code) if self.verbose: self.msg += ', id: {0}'.format(_id) self.msg += ', trigger: {0}'.format(trigger) self.msg += ', created date: {0}'.format(created_date) self.msg += ', last updated: {0}'.format(last_updated) self.msg += ', build_latency: {0}'.format(sec2human(build_latency)) self.msg += ', build URL: {0}'.format(build_url) self.msg += ' | build_latency={0:d}s'.format(build_latency)
class RestNagiosPlugin(NagiosPlugin): __version__ = __version__ # abstract class __metaclass__ = ABCMeta def __init__(self): # Python 2.x super(RestNagiosPlugin, self).__init__() # Python 3.x # super().__init__() self.name = None self.default_host = 'localhost' self.default_port = 80 self.default_user = None self.default_password = None self.host = None self.port = None self.user = None self.password = None self.protocol = 'http' self.msg = 'rest msg not defined yet' self.request = RequestHandler() self.req = None self.json_data = None self.path = None self.json = False self.ok() def add_options(self): self.add_hostoption(name=self.name, default_host=self.default_host, default_port=self.default_port) self.add_useroption(name=self.name, default_user=self.default_user, default_password=self.default_password) self.add_ssl_option() def add_ssl_option(self): self.add_opt('-S', '--use-ssl', action='store_true', default=False, help='Use SSL') def process_options(self): self.no_args() self.host = self.get_opt('host') self.port = self.get_opt('port') self.user = self.get_opt('user') self.password = self.get_opt('password') validate_host(self.host) validate_user(self.user) validate_password(self.password) validate_port(self.port) use_ssl = self.get_opt('use_ssl') log_option('ssl', use_ssl) if use_ssl and self.protocol == 'http': self.protocol = 'https' def run(self): self.req = self.query() if self.json: self.process_json(self.req.content) else: self.parse(self.req) def query(self): url = '{proto}://{host}:{port}/'.format(proto=self.protocol, host=self.host, port=self.port) if self.path: url += self.path.lstrip('/') auth = None if self.user and self.password: log.info('authenicating to rest API') auth = (self.user, self.password) req = self.request.get(url, auth=auth) return req #@abstractmethod def parse(self, req): pass #@abstractmethod def parse_json(self, json_data): pass def process_json(self, content): try: self.json_data = json.loads(content) if log.isEnabledFor(logging.DEBUG): log.debug('JSON prettified:\n\n%s\n%s', jsonpp(self.json_data), '='*80) return self.parse_json(self.json_data) except (KeyError, ValueError) as _: #raise UnknownError('{0}: {1}. {2}'.format(type(_).__name__, _, support_msg_api())) raise UnknownError('{0}. {1}'.format(self.exception_msg(), support_msg_api()))
class RestNagiosPlugin(NagiosPlugin): __version__ = __version__ # abstract class __metaclass__ = ABCMeta def __init__(self): # Python 2.x super(RestNagiosPlugin, self).__init__() # Python 3.x # super().__init__() self.name = None self.default_host = 'localhost' self.default_port = 80 self.default_user = None self.default_password = None self.host = None self.port = None self.user = None self.password = None self.protocol = 'http' self.msg = 'rest msg not defined yet' self.request = RequestHandler() self.request_method = 'get' self.req = None self.json_data = None self.path = None self.json = False self.headers = {} self.auth = True self.ok() def add_options(self): self.add_hostoption(name=self.name, default_host=self.default_host, default_port=self.default_port) if self.auth: self.add_useroption(name=self.name, default_user=self.default_user, default_password=self.default_password) self.add_opt('--kerberos', action='store_true', help='Kerberos SpNego authentication, uses TGT cache from $KRB5CCNAME or keytab ' + \ 'from $KRB5_CLIENT_KEYTAB environment variable if defined ' + \ '(overrides --user/--password)') self.add_ssl_option() def process_options(self): self.no_args() self.host = self.get_opt('host') self.port = self.get_opt('port') validate_host(self.host) validate_port(self.port) if self.auth and self.get_opt('kerberos'): self.auth = 'kerberos' if self.auth: self.user = self.get_opt('user') self.password = self.get_opt('password') if self.auth == 'optional': if self.user and self.password: validate_user(self.user) validate_password(self.password) elif self.auth == 'kerberos': if os.getenv('KRB5_CLIENT_KTNAME'): log.debug('kerberos enabled, will try to use keytab at %s', os.getenv('KRB5_CLIENT_KTNAME')) # if using KRB5_CLIENT_KTNAME to kinit avoid clobbering the same TGT cache /tmp/krb5cc_{uid} # as that may be used by different programs kinit'd different keytabs os.environ[ 'KRB5CCNAME'] = '/tmp/krb5cc_{euid}_{basename}'.format( euid=os.geteuid(), basename=prog) else: validate_user(self.user) validate_password(self.password) ssl_noverify = self.get_opt('ssl_noverify') if ssl_noverify: log_option('ssl no verify', 'true') ssl = 1 os.environ['SSL_NO_VERIFY'] = '1' # doesn't work, probably too late after instantiation #if not os.getenv('PYTHONWARNINGS'): # os.environ['PYTHONWARNINGS'] = 'ignore:Unverified HTTPS request' urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) else: ssl = self.get_opt('ssl') log_option('ssl', ssl) if ssl and self.protocol == 'http': self.protocol = 'https' if self.json: # recommended for many systems like CouchDB # but breaks Ambari API calls #self.headers['Accept'] = 'application/json' self.headers['Content-Type'] = 'application/json' def run(self): start_time = time.time() self.req = self.query() query_time = time.time() - start_time if self.json: log.info('parsing json response') self.process_json(self.req.content) else: log.info('parsing response') self.parse(self.req) if '|' not in self.msg: self.msg += ' |' if ' query_time=' not in self.msg: self.msg += ' query_time={0:.4f}s'.format(query_time) def query(self): url = '{proto}://{host}:{port}/'.format(proto=self.protocol, host=self.host, port=self.port) if self.path: url += self.path.lstrip('/') auth = None if self.auth == 'kerberos': log.info('authenticating to Rest API with Kerberos') auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL) elif self.user and self.password: log.info('authenticating to Rest API with username and password') auth = (self.user, self.password) # pylint: disable=redefined-variable-type kwargs = {} if os.getenv('SSL_NO_VERIFY'): kwargs['verify'] = False req = self.request.req(self.request_method, url, auth=auth, headers=self.headers, **kwargs) return req #@abstractmethod def parse(self, req): pass #@abstractmethod def parse_json(self, json_data): pass def process_json(self, content): try: self.json_data = json.loads(content) if log.isEnabledFor(logging.DEBUG): log.debug('JSON prettified:\n\n%s\n%s', jsonpp(self.json_data), '=' * 80) return self.parse_json(self.json_data) #except (KeyError, ValueError) as _: #raise UnknownError('{0}: {1}. {2}'.format(type(_).__name__, _, support_msg_api())) except (KeyError, ValueError): raise UnknownError('{0}. {1}'.format(self.exception_msg(), support_msg_api()))
def test_request_handler(self): req = RequestHandler().get('www.google.com') self.assertTrue(isinstance, requests.Response) RequestHandler(req)
class CheckDockerhubRepoBuildStatus(NagiosPlugin): def __init__(self): # Python 2.x super(CheckDockerhubRepoBuildStatus, self).__init__() # Python 3.x # super().__init__() self.request = RequestHandler() self.statuses = { '0': 'Queued', '3': 'Building', '10': 'Success', '-1': 'Error', '-4': 'Cancelled', } self.msg = 'DockerHub repo ' self.repo = None self.tag = None self.max_pages = None self.start_time = None self.ok() def add_options(self): self.add_opt('-r', '--repo', help="DockerHub repository to check, in form of '<user>/<repo>' eg. harisekhon/pytools") self.add_opt('-T', '--tag', help='Check status of only this tag eg. latest or 2.7') self.add_opt('-p', '--pages', default=1, metavar='num', help='Max number of API pages to iterate on to find the latest build (default: 1)' + \ '. If increasing this you will probably also need to increase --timeout') def process_options(self): self.repo = self.get_opt('repo') #validate_chars(self.repo, 'repo', 'A-Za-z0-9/_-') # official repos don't have slashes in them but then you can't check their build statuses either #if '/' not in self.repo: # self.usage('--repo must contain a slash (/) in it - ' + \ # 'official repos are not supported as DockerHub doesn\'t expose their build info') (namespace, repo) = self.repo.split('/', 1) validate_chars(namespace, 'namespace', 'A-Za-z0-9_-') validate_chars(repo, 'repo', 'A-Za-z0-9_-') self.repo = '{0}/{1}'.format(namespace, repo) # not needed as dashes and underscores are all that validation above permits through and they # are returned as is and processed successfully by DockerHub API #(user, repo) = self.repo.split('/', 1) #repo = urllib.quote_plus(repo) #self.repo = '{0}/{1}'.format(user, repo) self.tag = self.get_opt('tag') if self.tag is not None: # if you have a tag which characters other than these then please raise a ticket for extension at: # # https://github.com/harisekhon/nagios-plugins/issues # self.tag = self.tag.lstrip(':') validate_chars(self.tag, 'tag', r'A-Za-z0-9/\._-') #if not self.tag: # self.usage('--tag cannot be blank if given') self.max_pages = self.get_opt('pages') # if you have to iterate more than 20 pages you have problems, and this check will take ages validate_int(self.max_pages, 'max pages', 1, 20) self.max_pages = int(self.max_pages) def run(self): start_time = time.time() for page in range(1, self.max_pages + 1): url = 'https://registry.hub.docker.com/v2/repositories/{repo}/buildhistory?page={page}'\ .format(repo=self.repo, page=page) req = self.request.get(url) if log.isEnabledFor(logging.DEBUG): log.debug(jsonpp(req.content)) json_data = json.loads(req.content) log.debug('%s out of %s results returned for page %s', len(json_data['results']), json_data['count'], page) if self.process_results(json_data): # not quite as accurate as before as it now includes processing time but close enough query_time = time.time() - start_time if '|' not in self.msg: self.msg += ' |' self.msg += ' query_time={0:.2f}s'.format(query_time) return True extra_info = '' if self.verbose: extra_info = ' ({0} page{1} of API output)'\ .format(self.max_pages, plural(self.max_pages)) raise UnknownError('no completed builds found in last {0} builds{1}'.format(self.max_pages * 10, extra_info)) def process_results(self, json_data): for result in json_data['results']: tag = result['dockertag_name'] build_code = result['build_code'] _id = result['id'] # Skip Queued / Building as we're only interested in latest completed build status if int(result['status']) in (0, 3): if log.isEnabledFor(logging.DEBUG): log.debug("skipping queued/in progress build tag '%s', id: %s, build_code: %s", tag, _id, build_code) continue if self.tag and self.tag != tag: if log.isEnabledFor(logging.DEBUG): log.debug("skipping build tag '%s', id: %s, build_code: %s, does not match given --tag %s", tag, _id, build_code, self.tag) continue self.process_result(result) return True return False def process_result(self, result): _id = result['id'] log.info('latest build id: %s', _id) status = result['status'] log.info('status: %s', status) if not isInt(status, allow_negative=True): raise UnknownError('non-integer status returned by DockerHub API. {0}'.format(support_msg_api())) tag = result['dockertag_name'] log.info('tag: %s', tag) trigger = result['cause'] log.info('trigger: %s', trigger) created_date = result['created_date'] log.info('created date: %s', created_date) last_updated = result['last_updated'] log.info('last updated: %s', last_updated) created_datetime = datetime.datetime.strptime(created_date.split('.')[0], '%Y-%m-%dT%H:%M:%S') updated_datetime = datetime.datetime.strptime(last_updated.split('.')[0], '%Y-%m-%dT%H:%M:%S') build_latency_timedelta = updated_datetime - created_datetime build_latency = build_latency_timedelta.total_seconds() log.info('build latency (creation to last updated): %s', build_latency) # results in .0 floats anyway build_latency = int(build_latency) build_code = result['build_code'] build_url = 'https://hub.docker.com/r/{0}/builds/{1}'.format(self.repo, build_code) log.info('latest build URL: %s', build_url) if str(status) in self.statuses: status = self.statuses[str(status)] else: log.warning("status code '%s' not recognized! %s", status, support_msg_api()) log.warning('defaulting to assume status is an Error') status = 'Error' if status != 'Success': self.critical() self.msg += "'{repo}' last completed build status: '{status}', tag: '{tag}', build code: {build_code}"\ .format(repo=self.repo, status=status, tag=tag, build_code=build_code) if self.verbose: self.msg += ', id: {0}'.format(_id) self.msg += ', trigger: {0}'.format(trigger) self.msg += ', created date: {0}'.format(created_date) self.msg += ', last updated: {0}'.format(last_updated) self.msg += ', build_latency: {0}'.format(sec2human(build_latency)) self.msg += ', build URL: {0}'.format(build_url) self.msg += ' | build_latency={0:d}s'.format(build_latency)
class CheckConsulPeerCount(NagiosPlugin): def __init__(self): super(CheckConsulPeerCount, self).__init__() self.name = 'Consul' self.default_port = 8500 self.host = None self.port = None self.request_handler = RequestHandler() self.msg = 'NO MESSAGE DEFINED' def add_options(self): self.add_hostoption(name=self.name, default_host='localhost', default_port=self.default_port) self.add_thresholds(default_warning=1, default_critical=1) @staticmethod def get_peers(content): json_data = None try: json_data = json.loads(content) except ValueError: raise UnknownError("non-json data returned by consul: '%s'. %s" % (content, support_msg_api())) if not json_data: raise CriticalError('no peers found, recently started?') #if not json_data: # raise UnknownError("blank list returned by consul! '%s'. %s" % (content, support_msg_api())) if not isList(json_data): raise UnknownError("non-list returned by consul: '%s'. %s" % (content, support_msg_api())) for peer in json_data: log.debug('peer: {0}'.format(peer)) peers = uniq_list(json_data) return peers # closure factory @staticmethod def check_response_code(msg): def tmp(req): if req.status_code != 200: err = '' if req.content and isStr(req.content) and len(req.content.split('\n')) < 2: err += ': ' + req.content raise CriticalError("{0}: '{1}' {2}{3}".format(msg, req.status_code, req.reason, err)) return tmp def run(self): self.host = self.get_opt('host') self.port = self.get_opt('port') validate_host(self.host) validate_port(self.port) self.validate_thresholds(optional=True, simple='lower') url = 'http://%(host)s:%(port)s/v1/status/peers' % self.__dict__ req = self.request_handler.get(url) self.request_handler.check_response_code = \ self.check_response_code('failed to retrieve Consul peers') peers = self.get_peers(req.content) peer_count = len(peers) self.ok() self.msg = 'Consul peer count = {0}'.format(peer_count) self.check_thresholds(peer_count) self.msg += ' | consul_peer_count={0}'.format(peer_count) #self.msg += self.get_perf_thresholds(boundary='lower') self.msg += self.get_perf_thresholds(boundary='lower')
def __init__(self): super(ConsulKeyCheck, self).__init__() self.name = "Consul" self.default_port = 8500 self.request_handler = RequestHandler()
def get_status(self): url = 'http://%(host)s:%(port)s/oozie/v1/admin/status' % self.__dict__ req = RequestHandler().get(url) return self.parse(req)
class ConsulPeerCount(NagiosPlugin): def __init__(self): super(ConsulPeerCount, self).__init__() self.name = 'Consul' self.default_port = 8500 self.host = None self.port = None self.request_handler = RequestHandler() self.msg = 'NO MESSAGE DEFINED' def add_options(self): self.add_hostoption(name=self.name, default_host='localhost', default_port=self.default_port) self.add_thresholds(default_warning=1, default_critical=1) @staticmethod def get_peers(content): json_data = None try: json_data = json.loads(content) except ValueError: raise UnknownError("non-json data returned by consul: '%s'. %s" % (content, support_msg_api())) if not json_data: raise CriticalError('no peers found, recently started?') #if not json_data: # raise UnknownError("blank list returned by consul! '%s'. %s" % (content, support_msg_api())) if not isList(json_data): raise UnknownError("non-list returned by consul: '%s'. %s" % (content, support_msg_api())) for peer in json_data: log.debug('peer: {0}'.format(peer)) peers = uniq_list(json_data) return peers # closure factory @staticmethod def check_response_code(msg): def tmp(req): if req.status_code != 200: err = '' if req.content and isStr(req.content) and len(req.content.split('\n')) < 2: err += ': ' + req.content raise CriticalError("{0}: '{1}' {2}{3}".format(msg, req.status_code, req.reason, err)) return tmp def run(self): self.host = self.get_opt('host') self.port = self.get_opt('port') validate_host(self.host) validate_port(self.port) self.validate_thresholds(optional=True, simple='lower') url = 'http://%(host)s:%(port)s/v1/status/peers' % self.__dict__ req = self.request_handler.get(url) self.request_handler.check_response_code = \ self.check_response_code('failed to retrieve Consul peers') peers = self.get_peers(req.content) peer_count = len(peers) self.ok() self.msg = 'Consul peer count = {0}'.format(peer_count) self.check_thresholds(peer_count) self.msg += ' | consul_peer_count={0}'.format(peer_count) #self.msg += self.get_perf_thresholds(boundary='lower') self.msg += self.get_perf_thresholds(boundary='lower')