def check_http(self, host, port, url_path=''): if not isStr(url_path): url_path = '' url = '{protocol}://{host}:{port}/{url_path}'.format(protocol=self.protocol, host=host, port=port, url_path=url_path.lstrip('/')) log.info('GET %s', url) try: # timeout here isn't total timeout, it's response time req = requests.get(url, timeout=self.request_timeout) except requests.exceptions.RequestException as _: log.info('%s - returned exception: %s', url, _) return False except IOError as _: log.info('%s - returned IOError: %s', url, _) return False log.debug("%s - response: %s %s", url, req.status_code, req.reason) log.debug("%s - content:\n%s\n%s\n%s", url, '='*80, req.content.strip(), '='*80) if req.status_code != 200: log.info('%s - status code %s != 200', url, req.status_code) return None if self.regex: log.info('%s - checking regex against content', url) # if this ends up not being processed properly and remains a string instead # of the expected compiled regex, then .search() will hang assert not isStr(self.regex) if self.regex.search(req.content): log.info('%s - regex matched http output', url) else: log.info('%s - regex did not match http output', url) return None log.info("%s - passed all checks", url) return (host, port)
def run(self): self.no_args() host = self.get_opt('host') port = self.get_opt('port') warn_on_recent_start = self.get_opt('warn_on_recent_start') validate_host(host) validate_port(port) log.info('querying Tachyon%(name)s' % self.__dict__) url = 'http://%(host)s:%(port)s/home' % locals() log.debug('GET %s' % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit('CRITICAL', _) log.debug("response: %s %s" % (req.status_code, req.reason)) log.debug("content:\n{0}\n{1}\n{2}".format('=' * 80, req.content.strip(), '=' * 80)) if req.status_code != 200: qquit('CRITICAL', "%s %s" % (req.status_code, req.reason)) soup = BeautifulSoup(req.content, 'html.parser') try: uptime = soup.find('th', text=re.compile( 'Uptime:?', re.I)).find_next_sibling().get_text() version = soup.find('th', text=re.compile( 'Version:?', re.I)).find_next_sibling().get_text() except (AttributeError, TypeError): qquit( 'UNKNOWN', 'failed to find parse Tachyon%(name)s uptime/version info' % self.__dict__) if not uptime or not isStr(uptime) or not re.search( r'\d+\s+second', uptime): qquit( 'UNKNOWN', 'Tachyon{0} uptime format not recognized: {1}'.format( self.name, uptime)) if not isVersion(version): qquit( 'UNKNOWN', 'Tachyon{0} version format not recognized: {1}'.format( self.name, version)) self.msg = 'Tachyon{0} version: {1}, uptime: {2}'.format( self.name, version, uptime) # pylint: disable=attribute-defined-outside-init self.ok() if warn_on_recent_start: match = re.match( r'^(\d+)\s+day[^\d\s]+\s+(\d+)\s+hour[^\d\s]+\s+(\d+)\s+minute', uptime, re.I) if match: days = int(match.group(1)) hours = int(match.group(2)) mins = int(match.group(3)) if days == 0 and hours == 0 and mins < 30: self.warning() self.msg += ' (< 30 mins)' else: self.unknown() self.msg += " (couldn't determine if uptime < 30 mins)"
def req(self, url, method='post', body=None): assert isStr(method) log.debug('%s %s', method.upper(), url) headers = {"Content-Type": "application/json", "Accept": "application/json", "JSESSIONID": self.jsessionid} log.debug('headers: %s', headers) start_time = time.time() try: req = getattr(requests, method.lower())(url, #cookies=self.jar, data=body, headers=headers) for cookie_tuple in req.cookies.items(): if cookie_tuple[0] == 'JSESSIONID': self.jsessionid = cookie_tuple[1].rstrip('/') timing = time.time() - start_time except requests.exceptions.RequestException as _: qquit('CRITICAL', _) if log.isEnabledFor(logging.DEBUG): log.debug("response: %s %s", req.status_code, req.reason) content = req.content try: content = jsonpp(req.content).strip() except ValueError: pass log.debug("content:\n%s\n%s\n%s", '='*80, content, '='*80) if req.status_code != 200: info = '' try: info = ': {0}'.format(json.loads(req.content)['result']) except (KeyError, ValueError): pass qquit('CRITICAL', "%s %s%s" % (req.status_code, req.reason, info)) return (req, timing)
def add_thresholds(self, name='', default_warning=None, default_critical=None, percent=False): if not isStr(name): raise CodingError('non-string passed as name argument to add_thresholds()') name = re.sub('[^A-Za-z0-9]', '-', name).lower() default_warning_msg = '' default_critical_msg = '' unit = '' if percent: unit = '%' if default_warning is not None: default_warning_msg = ', default: {0}{1}'.format(default_warning, unit) if default_critical is not None: default_critical_msg = ', default: {0}{1}'.format(default_critical, unit) if name: self.add_opt('--{0}-warning'.format(name), metavar='N', default=default_warning, help='{0} warning threshold or ra:nge (inclusive{1})' .format(name.title(), default_warning_msg)) self.add_opt('--{0}-critical'.format(name), metavar='N', default=default_critical, help='{0} critical threshold or ra:nge (inclusive{1})' .format(name.title(), default_critical_msg)) else: self.add_opt('-w', '--warning', metavar='N', default=default_warning, help='Warning threshold or ra:nge (inclusive{0})'.format(default_warning_msg)) self.add_opt('-c', '--critical', metavar='N', default=default_critical, help='Critical threshold or ra:nge (inclusive{0})'.format(default_critical_msg))
def parse_host_name(self, item): # pylint: disable=no-self-use if isStr(item): item = json.loads(item) try: return item['Hosts']['host_name'] except KeyError as _: qquit('CRITICAL', 'failed to parse Ambari host name: %s' % _)
def parse_cluster_name(item): if isStr(item): item = json.loads(item) try: return item['Clusters']['cluster_name'] except KeyError as _: qquit('CRITICAL', 'failed to parse Ambari cluster name: %s' % _)
def parse_host_name(item): if isStr(item): item = json.loads(item) try: return item['Hosts']['host_name'] except KeyError as _: qquit('CRITICAL', 'failed to parse Ambari host name: %s' % _)
def parse_blueprint_name(item): if isStr(item): item = json.loads(item) try: return item['Blueprints']['blueprint_name'] except KeyError as _: qquit('CRITICAL', 'failed to parse Ambari blueprint name: %s' % _)
def check_http(self, host, port, url_path=''): if not isStr(url_path): url_path = '' url = '{protocol}://{host}:{port}/{url_path}'.format(protocol=self.protocol, host=host, port=port, url_path=url_path.lstrip('/')) log.info('GET %s', url) try: # timeout here isn't total timeout, it's response time req = requests.get(url, timeout=self.request_timeout) except requests.exceptions.RequestException: return False except IOError: return False log.debug("%s - response: %s %s", url, req.status_code, req.reason) log.debug("%s - content:\n%s\n%s\n%s", url, '='*80, req.content.strip(), '='*80) if req.status_code != 200: return None if self.regex: log.info('%s - checking regex against content', url) if self.regex.search(req.content): log.info('%s - regex matched http output', url) else: log.info('%s - regex did not match http output', url) return None log.info("%s - passed all checks", url) return (host, port)
def check_http(self, host, port, url_path=''): if not isStr(url_path): url_path = '' url = '{protocol}://{host}:{port}/{url_path}'.format( protocol=self.protocol, host=host, port=port, url_path=url_path.lstrip('/')) log.info('GET %s', url) try: # timeout here isn't total timeout, it's response time req = requests.get(url, timeout=self.request_timeout) except requests.exceptions.RequestException: return False except IOError: return False log.debug("%s - response: %s %s", url, req.status_code, req.reason) log.debug("%s - content:\n%s\n%s\n%s", url, '=' * 80, req.content.strip(), '=' * 80) if req.status_code != 200: return None if self.regex: log.info('%s - checking regex against content', url) if self.regex.search(req.content): log.info('%s - regex matched http output', url) else: log.info('%s - regex did not match http output', url) return None log.info("%s - passed all checks", url) return (host, port)
def validate_thresholds(self, name='', warning=None, critical=None, **kwargs): if not isStr(name): raise CodingError('non-string name passed to validate_thresholds()') if name: name += '_' self.validate_threshold('{0}{1}'.format(name, 'warning'), warning, **kwargs) self.validate_threshold('{0}{1}'.format(name, 'critical'), critical, **kwargs)
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))
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 prepare_regex(self): self.compile('hostname', r'(?<!\w\]\s)' + \ r'(?<!\.)' + \ # ignore Java methods such as SomeClass$method:20 r'(?<!\$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ # don't match 2018-01-01T00:00:00 => 2018-01-<hostname>:00:00 r'(?!\d+T\d+:\d+)' + \ r'(?!\d+[^A-Za-z0-9]|' + \ self.custom_ignores_raw + ')' + \ '(' + hostname_regex + ')' + \ self.negative_host_lookbehind + r':(\d{1,5}(?!\.?\w))', ) self.compile('domain', # don't match java -some.net.property #r'(?<!-)' + \ r'(?!' + self.custom_ignores_raw + ')' + \ domain_regex_strict + \ # don't match java -Dsome.net.property= r'(?!=)' + \ r'(?!\.[A-Za-z])(\b|$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ self.negative_host_lookbehind ) self.compile('fqdn', # don't match java -some.net.property #r'(?<!-)' + \ r'(?!' + self.custom_ignores_raw + ')' + \ '(' + fqdn_regex + ')' + \ # don't match java -Dsome.net.property= r'(?!=)' + \ r'(?!\.[A-Za-z])(\b|$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ self.negative_host_lookbehind ) re_regex_ending = re.compile('_regex$') # auto-populate any *_regex to self.regex[name] = name_regex for _ in globals(): if re_regex_ending.search(_) and \ re_regex_ending.sub('', _) not in self.regex: self.regex[re.sub('_regex$', '', _)] = globals()[_] for _ in self.regex: if isStr(self.regex[_]): self.compile(_, self.regex[_])
def check_thresholds(self, result, name=''): if not isStr(name): raise CodingError('non-string passed to check_thresholds()') if name: name += '_' threshold_breach_msg = self.check_threshold('{0}critical'.format(name), result) threshold_breach_msg2 = self.check_threshold('{0}warning'.format(name), result) if threshold_breach_msg: self.msg += ' ' + threshold_breach_msg elif threshold_breach_msg2: self.msg += ' ' + threshold_breach_msg2
def validate_thresholds(self, name='', warning=None, critical=None, **kwargs): if not isStr(name): raise CodingError('non-string name passed to validate_thresholds()') if name: name += '_' if 'percent' in kwargs and kwargs['percent']: del kwargs['percent'] kwargs['min'] = 0 kwargs['max'] = 100 self.validate_threshold(name='{0}{1}'.format(name, 'warning'), threshold=warning, **kwargs) self.validate_threshold(name='{0}{1}'.format(name, 'critical'), threshold=critical, **kwargs)
def validate_thresholds(self, name='', warning=None, critical=None, **kwargs): if not isStr(name): raise CodingError('non-string name passed to validate_thresholds()') if name: name += '_' if 'percent' in kwargs and kwargs['percent']: del kwargs['percent'] kwargs['min'] = 0 kwargs['max'] = 100 kwargs['integer'] = False self.validate_threshold(name='{0}{1}'.format(name, 'warning'), threshold=warning, **kwargs) self.validate_threshold(name='{0}{1}'.format(name, 'critical'), threshold=critical, **kwargs)
def anonymize_dynamic(self, name, line): #log.debug('anonymize_dynamic(%s, %s)', name, line) if not isStr(line): raise AssertionError('anonymize_dynamic: passed in non-string line: {}'.format(line)) line = self.dynamic_replace(name, line) for i in range(2, 101): name2 = '{}{}'.format(name, i) if name2 in self.regex: line = self.dynamic_replace(name2, line) else: break return line
def prepare_regex(self): self.compile('hostname', r'(?<!\w\]\s)' + \ r'(?<!\.)' + \ # ignore Java methods such as SomeClass$method:20 r'(?<!\$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ # don't match 2018-01-01T00:00:00 => 2018-01-<hostname>:00:00 r'(?!\d+T\d+:\d+)' + \ r'(?!\d+[^A-Za-z0-9]|' + \ self.custom_ignores_raw + ')' + \ hostname_regex + \ self.negative_host_lookbehind + r':(\d{1,5}(?!\.?\w))', ) self.compile('domain', r'(?!' + self.custom_ignores_raw + ')' + \ domain_regex_strict + \ r'(?!\.[A-Za-z])(\b|$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ self.negative_host_lookbehind ) self.compile('fqdn', r'(?!' + self.custom_ignores_raw + ')' + \ fqdn_regex + \ r'(?!\.[A-Za-z])(\b|$)' + \ # ignore Java stack traces eg. at SomeClass(Thread.java;789) r'(?!\(\w+\.java:\d+\))' + \ self.negative_host_lookbehind ) ip_regex_strict = r'(?<!\d\.)' + ip_regex + r'(?![^:/]\d+)' self.compile('ip', ip_regex_strict + r'/\d{1,2}') self.compile('ip2', ip_regex_strict) self.compile('subnet_mask', r'(?<!\d\.)' + subnet_mask_regex + r'(?![^:]\d+)') self.compile('ip_prefix', r'(?<!\d\.)' + ip_prefix_regex + r'(?!\.\d+\.\d+)') re_regex_ending = re.compile('_regex$') # auto-populate any *_regex to self.regex[name] = name_regex for _ in globals(): if re_regex_ending.search(_) and \ re_regex_ending.sub('', _) not in self.regex: self.regex[re.sub('_regex$', '', _)] = globals()[_] for _ in self.regex: if isStr(self.regex[_]): self.compile(_, self.regex[_])
def collect_results(self): return_val = None for _ in self.host_list: return_val = self.que.get() if return_val: break if return_val: if isTuple(return_val): self.finish(*return_val) elif isStr(return_val): self.finish(return_val) else: code_error('collect_results() found non-tuple / non-string on que')
def collect_results(self): return_val = None for _ in self.host_list: return_val = self.queue.get() if return_val: break if return_val: if isTuple(return_val): self.finish(*return_val) elif isStr(return_val): self.finish(return_val) else: code_error('collect_results() found non-tuple / non-string on que')
def validate_threshold(self, name, threshold=None, optional=False, **kwargs): if not isStr(name): raise CodingError('non-string name passed to validate_threshold()') if threshold is None: # log.debug("using threshold option '%s'", name) threshold = self.get_opt(name) # log.debug("got threshold '%s'", threshold) if optional and threshold is None: return None else: try: self.__thresholds[name] = Threshold(threshold, name=name, **kwargs) except InvalidThresholdException as _: self.usage(_) log_option(name, threshold)
def validate_threshold(self, name, threshold=None, optional=False, **kwargs): if not isStr(name): raise CodingError('non-string name passed to validate_threshold()') name = re.sub('[^A-Za-z0-9]', '_', name).lower() if threshold is None: # log.debug("using threshold option '%s'", name) threshold = self.get_opt(name) # log.debug("got threshold '%s'", threshold) if optional and threshold is None: return None else: try: self.__thresholds[name] = Threshold(threshold, name=name, **kwargs) except InvalidThresholdException as _: self.usage(_) log_option(name, threshold)
def run(self): self.no_args() host = self.options.host port = self.options.port validate_host(host) validate_port(port) key = self.options.key regex = self.options.regex if not key: self.usage('--key not defined') key = key.lstrip('/') validate_chars(key, 'key', r'\w\/-') if regex: validate_regex(regex, 'key') self.validate_thresholds(optional=True) req = None url = 'http://%(host)s:%(port)s/v1/kv/%(key)s' % locals() log.debug('GET %s' % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit('CRITICAL', _) log.debug("response: %s %s" % (req.status_code, req.reason)) log.debug("content: '%s'" % req.content) if req.status_code != 200: err = '' if req.content and isStr( req.content) and len(req.content.split('\n')) < 2: err += ': ' + req.content qquit( 'CRITICAL', "failed to retrieve consul key '%s': '%s' %s%s" % (key, req.status_code, req.reason, err)) value = self.extract_value(req.content) log.info("value = '%(value)s'" % locals()) self.ok() self.msg = "consul key '%s' value = '%s'" % (key, value) if regex: if not re.search(regex, value): self.critical() self.msg += " (did not match expected regex '%s')" % regex #elif self.get_verbose(): # self.msg += " (matched regex '%s')" % regex self.check_thresholds(value) if isFloat(value): self.msg += " | '%s'=%s" % (key, value)
def run(self): self.no_args() host = self.get_opt("host") port = self.get_opt("port") warn_on_recent_start = self.get_opt("warn_on_recent_start") validate_host(host) validate_port(port) log.info("querying Tachyon%(name)s" % self.__dict__) url = "http://%(host)s:%(port)s/home" % locals() log.debug("GET %s" % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit("CRITICAL", _) log.debug("response: %s %s" % (req.status_code, req.reason)) log.debug("content:\n{0}\n{1}\n{2}".format("=" * 80, req.content.strip(), "=" * 80)) if req.status_code != 200: qquit("CRITICAL", "Non-200 response! %s %s" % (req.status_code, req.reason)) soup = BeautifulSoup(req.content, "html.parser") try: uptime = soup.find("th", text=re.compile("Uptime:?", re.I)).find_next_sibling().get_text() version = soup.find("th", text=re.compile("Version:?", re.I)).find_next_sibling().get_text() except (AttributeError, TypeError): qquit("UNKNOWN", "failed to find parse Tachyon%(name)s uptime/version info" % self.__dict__) if not uptime or not isStr(uptime) or not re.search(r"\d+\s+second", uptime): qquit("UNKNOWN", "Tachyon{0} uptime format not recognized: {1}".format(self.name, uptime)) if not isVersion(version): qquit("UNKNOWN", "Tachyon{0} version format not recognized: {1}".format(self.name, version)) self.msg = "Tachyon{0} version: {1}, uptime: {2}".format( self.name, version, uptime ) # pylint: disable=attribute-defined-outside-init self.ok() if warn_on_recent_start: match = re.match(r"^(\d+)\s+day[^\d\s]+\s+(\d+)\s+hour[^\d\s]+\s+(\d+)\s+minute", uptime, re.I) if match: days = int(match.group(1)) hours = int(match.group(2)) mins = int(match.group(3)) if days == 0 and hours == 0 and mins < 30: self.warning() self.msg += " (< 30 mins)" else: self.unknown() self.msg += " (couldn't determine if uptime < 30 mins)"
def run(self): self.no_args() host = self.get_opt('host') port = self.get_opt('port') warn_on_recent_start = self.get_opt('warn_on_recent_start') validate_host(host) validate_port(port) log.info('querying %s%s', self.software, self.name) url = 'http://%(host)s:%(port)s/home' % locals() log.debug('GET %s', url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit('CRITICAL', _) log.debug("response: %s %s", req.status_code, req.reason) log.debug("content:\n%s\n%s\n%s", '='*80, req.content.strip(), '='*80) if req.status_code != 200: qquit('CRITICAL', "%s %s" % (req.status_code, req.reason)) soup = BeautifulSoup(req.content, 'html.parser') try: uptime = soup.find('th', text=re.compile('Uptime:?', re.I)).find_next_sibling().get_text() version = soup.find('th', text=re.compile('Version:?', re.I)).find_next_sibling().get_text() except (AttributeError, TypeError): qquit('UNKNOWN', 'failed to find parse %(software)s%(name)s uptime/version info' % self.__dict__) if not uptime or not isStr(uptime) or not re.search(r'\d+\s+second', uptime): qquit('UNKNOWN', '{0}{1} uptime format not recognized: {2}'.format(self.software, self.name, uptime)) if not isVersion(version): qquit('UNKNOWN', '{0}{1} version format not recognized: {2}'.format(self.software, self.name, version)) self.msg = '{0}{1} version: {2}, uptime: {3}'.format(self.software, self.name, version, uptime) # pylint: disable=attribute-defined-outside-init self.ok() if warn_on_recent_start: match = re.match(r'^(\d+)\s+day[^\d\s]+\s+(\d+)\s+hour[^\d\s]+\s+(\d+)\s+minute', uptime, re.I) if match: days = int(match.group(1)) hours = int(match.group(2)) mins = int(match.group(3)) if days == 0 and hours == 0 and mins < 30: self.warning() self.msg += ' (< 30 mins)' else: self.unknown() self.msg += " (couldn't determine if uptime < 30 mins)"
def run(self): self.no_args() host = self.get_opt('host') port = self.get_opt('port') validate_host(host) validate_port(port) key = self.get_opt('key') regex = self.get_opt('regex') if not key: self.usage('--key not defined') key = key.lstrip('/') validate_chars(key, 'key', r'\w\/-') if regex: validate_regex(regex, 'key') self.validate_thresholds(optional=True) req = None url = 'http://%(host)s:%(port)s/v1/kv/%(key)s' % locals() log.debug('GET %s' % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit('CRITICAL', _) log.debug("response: %s %s" % (req.status_code, req.reason)) log.debug("content: '%s'" % req.content) if req.status_code != 200: err = '' if req.content and isStr(req.content) and len(req.content.split('\n')) < 2: err += ': ' + req.content qquit('CRITICAL', "failed to retrieve consul key '%s': '%s' %s%s" % (key, req.status_code, req.reason, err)) value = self.extract_value(req.content) log.info("value = '%(value)s'" % locals()) self.ok() self.msg = "consul key '%s' value = '%s'" % (key, value) if regex: if not re.search(regex, value): self.critical() self.msg += " (did not match expected regex '%s')" % regex #elif self.get_verbose(): # self.msg += " (matched regex '%s')" % regex self.check_thresholds(value) if isFloat(value): self.msg += " | '%s'=%s" % (key, value)
def run(self): self.no_args() host = self.options.host port = self.options.port validate_host(host) validate_port(port) key = self.options.key regex = self.options.regex if not key: self.usage("--key not defined") key = key.lstrip("/") validate_chars(key, "key", r"\w\/-") if regex: validate_regex(regex, "key") self.validate_thresholds(optional=True) req = None url = "http://%(host)s:%(port)s/v1/kv/%(key)s" % locals() log.debug("GET %s" % url) try: req = requests.get(url) except requests.exceptions.RequestException as _: qquit("CRITICAL", _) log.debug("response: %s %s" % (req.status_code, req.reason)) log.debug("content: '%s'" % req.content) if req.status_code != 200: err = "" if req.content and isStr(req.content) and len(req.content.split("\n")) < 2: err += ": " + req.content qquit("CRITICAL", "failed to retrieve consul key '%s': '%s' %s%s" % (key, req.status_code, req.reason, err)) value = self.extract_value(req.content) log.info("value = '%(value)s'" % locals()) self.ok() self.msg = "consul key '%s' value = '%s'" % (key, value) if regex: if not re.search(regex, value): self.critical() self.msg += " (did not match expected regex '%s')" % regex # elif self.get_verbose(): # self.msg += " (matched regex '%s')" % regex self.check_thresholds(value) if isFloat(value): self.msg += " | '%s'=%s" % (key, value)
def get_opt(self, name): if not isStr(name): raise CodingError('passed non-string as arg to CLI.get_opt()') if not self.is_option_defined(name): raise CodingError('{0} option not defined'.format(name)) return getattr(self.options, name)
def handle_event(self): if isStr(self.command): print(os.popen(self.command).read(), end='') for line in sys.stdin: # do something with the data log.debug('data: %s' % line.strip())
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))