def jolokia(self, read_requests, raise_error=True): ''' :param read_requests: see https://jolokia.org/reference/html/protocol.html#post-request :type read_requests: list :param raise_error: :return: Jolokia response ''' def set_read_type(x): x['type'] = 'read' # hack quick verify if (not self.url.endswith('jolokia/')) or ('?' in self.url) or ( '&' in self.url): raise HttpError( "URL needs to end in jolokia/ and not contain ? and &", self.url) map(set_read_type, read_requests) r = self.__request(post_data=read_requests, raise_error=raise_error) try: return r.json() except Exception, e: raise HttpError(str(e), self.url), None, sys.exc_info()[2]
def jolokia(self, read_requests, raise_error=True): ''' :param read_requests: see https://jolokia.org/reference/html/protocol.html#post-request :type read_requests: list :param raise_error: :return: Jolokia response ''' def set_read_type(x): x['type'] = 'read' # hack quick verify parsed_url = urlparse.urlsplit(self.url) if (not parsed_url.path.endswith('/jolokia/')) or ( '?' in self.url) or ('&' in self.url): raise HttpError( "URL needs to end in /jolokia/ and not contain ? and &", self.url) map(set_read_type, read_requests) for rr in read_requests: if 'mbean' not in rr: raise CheckError('missing "mbean" key in read request') r = self.__request(post_data=read_requests, raise_error=raise_error) try: return r.json() except Exception, e: raise HttpError(str(e), self.url), None, sys.exc_info()[2]
def __request(self, raise_error=True, post_data=None): if self.__r is None: if self.max_retries: s = requests.Session() s.mount('', HTTPAdapter(max_retries=self.max_retries)) else: s = requests base_url = self.url basic_auth = None url_parsed = urlparse.urlsplit(base_url) if url_parsed and url_parsed.username and url_parsed.password: base_url = base_url.replace( "{0}:{1}@".format(urllib.quote(url_parsed.username), urllib.quote(url_parsed.password)), "") base_url = base_url.replace( "{0}:{1}@".format(url_parsed.username, url_parsed.password), "") basic_auth = (url_parsed.username, url_parsed.password) self.clean_url = base_url if self.oauth2: self._headers.update({ 'Authorization': 'Bearer {}'.format(tokens.get(self.oauth2_token_name)) }) self._headers.update({'User-Agent': get_user_agent()}) try: if post_data is None: # GET or HEAD get_method = getattr(s, self.__method) self.__r = get_method(base_url, params=self.params, timeout=self.timeout, verify=self.verify, headers=self._headers, auth=basic_auth, allow_redirects=self.allow_redirects) else: self.__r = s.post(base_url, params=self.params, timeout=self.timeout, verify=self.verify, headers=self._headers, auth=basic_auth, data=json.dumps(post_data)) except requests.Timeout, e: raise HttpError('timeout', self.clean_url), None, sys.exc_info()[2] except requests.ConnectionError, e: raise HttpError('connection failed', self.clean_url), None, sys.exc_info()[2]
def __request(self, url, params=None, body=None): """Return json response""" if self.oauth2: self._headers.update( {'Authorization': 'Bearer {}'.format(tokens.get('uid'))}) try: if body is None: response = requests.get(url, params=params, timeout=self.timeout, headers=self._headers) if not response.ok: raise Exception( 'Elasticsearch query failed: {}'.format(url)) return response.json() else: response = requests.post(url, params=params, json=body, timeout=self.timeout, headers=self._headers) if not response.ok: raise Exception( 'Elasticsearch query failed: {} with response: {}'. format(url, json.dumps(response.text))) return response.json() except requests.Timeout: raise HttpError('timeout', self.url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.url), None, sys.exc_info()[2] except Exception: raise
def _request(self, endpoint, q, method='get'): try: url = urlparse.urljoin(self.__service_url, endpoint) request = getattr(self.__session, method.lower()) if method.lower() == 'post': response = request(url, json=q) else: response = request(url, params={'query': json.dumps(q)}) if response.ok: return response.json() else: raise CheckError( 'EntitiesWrapper query failed: {} with status {}:{}'. format(q, response.status_code, response.text)) except requests.Timeout: raise HttpError('timeout', self.__service_url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.__service_url), None, sys.exc_info()[2]
def actuator_metrics(self, prefix='zmon.response.', raise_error=True): """ /metric responds with keys like: zmon.response.<status>.<method>.<end-point> Response map is ep->method->status->metric """ response = self.json(raise_error=raise_error) if not isinstance(response, dict): raise HttpError('Invalid actuator metrics: response must be a JSON object', self.url) # for clojure projects we use the dropwizard servlet, there the json looks slightly different if "timers" in response: return map_dropwizard_timers(response['timers'], prefix) else: return map_spring_boot_metrics(response, prefix)
def healthrule_violations(self, application, time_range_type=BEFORE_NOW, duration_in_mins=5, start_time=None, end_time=None, severity=None): """ Return Healthrule violations for AppDynamics application. :param application: Application name or ID :type application: str :param time_range_type: Valid time range type. Valid range types are BEFORE_NOW, BEFORE_TIME, AFTER_TIME and BETWEEN_TIMES. Default is BEFORE_NOW. :type time_range_type: str :param duration_in_mins: Time duration in mins. Required for BEFORE_NOW, AFTER_TIME, BEFORE_TIME range types. :type duration_in_mins: int :param start_time: Start time (in milliseconds) from which the metric data is returned. Default is 5 mins ago. :type start_time: int :param end_time: End time (in milliseconds) until which the metric data is returned. Default is now. :type end_time: int :param severity: Filter results based on severity. Valid values CRITICAL or WARNING. :type severity: str :return: List of healthrule violations :rtype: list """ try: if severity is not None and severity not in SEVERITIES: raise Exception( 'Invalid severity! Allowed values are: {}'.format( ','.join(SEVERITIES))) params = self._prepare_time_range_params(time_range_type, duration_in_mins, start_time, end_time) resp = self.__session.get( self.healthrule_violations_url(application), params=params) resp.raise_for_status() json_resp = resp.json() if severity: # we need some filtering! return [e for e in json_resp if e['severity'] == severity] return json_resp except requests.Timeout: raise HttpError('timeout', self.url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.url), None, sys.exc_info()[2] except: logger.exception('AppDynamics request failed') raise
def metric_data(self, application, metric_path, time_range_type=BEFORE_NOW, duration_in_mins=5, start_time=None, end_time=None, rollup=True): """ AppDynamics's metric-data API :param application: Application name or ID :type application: str :param metric_path: The path to the metric in the metric hierarchy :type metric_path: str :param time_range_type: Valid time range type. Valid range types are BEFORE_NOW, BEFORE_TIME, AFTER_TIME and BETWEEN_TIMES. Default is BEFORE_NOW. :type time_range_type: str :param duration_in_mins: Time duration in mins. Required for BEFORE_NOW, AFTER_TIME, BEFORE_TIME range types. :type duration_in_mins: int :param start_time: Start time (in milliseconds) from which the metric data is returned. Default is 5 mins ago. :type start_time: int :param end_time: End time (in milliseconds) until which the metric data is returned. Default is now. :type end_time: int :param rollup: By default, the values of the returned metrics are rolled up into a single data point (rollup=true). To get separate results for all values within the time range, set the rollup parameter to false. :type rollup: bool :return: metric values for a metric :rtype: list """ try: if application is None: raise Exception('Argument application is mandatory') if metric_path is None: raise Exception('Argument metric_path is mandatory.') params = self._prepare_time_range_params(time_range_type, duration_in_mins, start_time, end_time) params['metric-path'] = metric_path params['rollup'] = rollup resp = self.__session.get(self.metric_data_url(application), params=params) resp.raise_for_status() return resp.json() except requests.Timeout: raise HttpError('timeout', self.url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.url), None, sys.exc_info()[2] except: logger.exception('AppDynamics request failed') raise
def json(self, raise_error=True): r = self.__request(raise_error=raise_error) try: return r.json() except Exception, e: raise HttpError(str(e), self.url), None, sys.exc_info()[2]
class HttpWrapper(object): def __init__( self, url, method='GET', params=None, base_url=None, timeout=10, max_retries=0, allow_redirects=None, verify=True, oauth2=False, oauth2_token_name='uid', headers=None, ): if method.lower() not in ('get', 'head'): raise CheckError( 'Invalid method. Only GET and HEAD are supported!') if not base_url and not absolute_http_url(url): # More verbose error message! raise ConfigurationError( 'HTTP wrapper improperly configured. Invalid base_url. Check entity["url"] or call with absolute url.' ) self.url = (base_url + url if not absolute_http_url(url) else url) self.clean_url = None self.params = params self.timeout = timeout self.max_retries = max_retries self.verify = verify self._headers = headers or {} self.oauth2 = oauth2 self.oauth2_token_name = oauth2_token_name self.__method = method.lower() self.allow_redirects = True if allow_redirects is None else allow_redirects if self.__method == 'head' and allow_redirects is None: self.allow_redirects = False self.__r = None def __request(self, raise_error=True, post_data=None): if self.__r is None: if self.max_retries: s = requests.Session() s.mount('', HTTPAdapter(max_retries=self.max_retries)) else: s = requests base_url = self.url basic_auth = None url_parsed = urlparse.urlsplit(base_url) if url_parsed and url_parsed.username and url_parsed.password: base_url = base_url.replace( "{0}:{1}@".format(urllib.quote(url_parsed.username), urllib.quote(url_parsed.password)), "") base_url = base_url.replace( "{0}:{1}@".format(url_parsed.username, url_parsed.password), "") basic_auth = (url_parsed.username, url_parsed.password) self.clean_url = base_url if self.oauth2: self._headers.update({ 'Authorization': 'Bearer {}'.format(tokens.get(self.oauth2_token_name)) }) self._headers.update({'User-Agent': get_user_agent()}) try: if post_data is None: # GET or HEAD get_method = getattr(s, self.__method) self.__r = get_method(base_url, params=self.params, timeout=self.timeout, verify=self.verify, headers=self._headers, auth=basic_auth, allow_redirects=self.allow_redirects) else: self.__r = s.post(base_url, params=self.params, timeout=self.timeout, verify=self.verify, headers=self._headers, auth=basic_auth, data=json.dumps(post_data)) except requests.Timeout, e: raise HttpError('timeout', self.clean_url), None, sys.exc_info()[2] except requests.ConnectionError, e: raise HttpError('connection failed', self.clean_url), None, sys.exc_info()[2] except Exception, e: raise HttpError(str(e), self.clean_url), None, sys.exc_info()[2]
auth=basic_auth, data=json.dumps(post_data)) except requests.Timeout, e: raise HttpError('timeout', self.clean_url), None, sys.exc_info()[2] except requests.ConnectionError, e: raise HttpError('connection failed', self.clean_url), None, sys.exc_info()[2] except Exception, e: raise HttpError(str(e), self.clean_url), None, sys.exc_info()[2] if raise_error: try: self.__r.raise_for_status() except requests.HTTPError, e: raise HttpError(str(e), self.clean_url), None, sys.exc_info()[2] return self.__r def json(self, raise_error=True): r = self.__request(raise_error=raise_error) try: return r.json() except Exception, e: raise HttpError(str(e), self.url), None, sys.exc_info()[2] def jolokia(self, read_requests, raise_error=True): ''' :param read_requests: see https://jolokia.org/reference/html/protocol.html#post-request :type read_requests: list :param raise_error: :return: Jolokia response
def query_batch(self, metrics, start=5, end=0, time_unit='minutes', start_absolute=None, end_absolute=None): """ Query kairosdb for several checks at once. :param metrics: list of KairosDB metric queries, one query per metric name. [ { 'name': 'metric_name', # name of the metric 'group_by': ['foo'], # list of fields to group by 'aggregators': [ # list of aggregator objects { # structure of a single aggregator 'name': 'max', 'sampling': { 'value': '1', 'unit': 'minutes' }, 'align_sampling': True } ], 'tags': { # dict with filtering tags 'key': ['max'] # a key is a tag name, list of values is used to filter # all the records with given tag and given values } } ] :type metrics: dict :param start: Relative start time. Default is 5. :type start: int :param end: End time. Default is 0. :type end: int :param time_unit: Time unit ('seconds', 'minutes', 'hours'). Default is 'minutes'. :type time_unit: str. :param start_absolute: Absolute start time in milliseconds, overrides the start parameter which is relative :type start_absolute: long :param end_absolute: Absolute end time in milliseconds, overrides the end parameter which is relative :type end_absolute: long :return: Array of results for each queried metric :rtype: list """ url = os.path.join(self.url, DATAPOINTS_ENDPOINT) if start < 1 or end < 0: raise ValueError( 'Time relative "start" and "end" must be greater than or equal to 1' ) q = {'metrics': metrics} if start_absolute is None: q['start_relative'] = {'value': start, 'unit': time_unit} else: q['start_absolute'] = start_absolute if end_absolute is None: if end > 0: q['end_relative'] = {'value': end, 'unit': time_unit} else: q['end_absolute'] = end_absolute try: response = self.__session.post(url, json=q) if response.ok: return response.json()['queries'] else: raise Exception( 'KairosDB Query failed: {} with status {}:{}'.format( q, response.status_code, response.text)) except requests.Timeout: raise HttpError('timeout', self.url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.url), None, sys.exc_info()[2]
def query(self, name, group_by=None, tags=None, start=5, end=0, time_unit='minutes', aggregators=None): """ Query kairosdb. :param name: Metric name. :type name: str :param group_by: List of fields to group by. :type group_by: list :param tags: Filtering tags. :type tags: dict :param start: Relative start time. Default is 5. :type start: int :param end: End time. Default is 0. :type end: int :param time_unit: Time unit ('seconds', 'minutes', 'hours'). Default is 'minutes'. :type time_unit: str. :param aggregators: List of aggregators. :type aggregators: list :return: Result queries. :rtype: dict """ url = os.path.join(self.url, DATAPOINTS_ENDPOINT) if start < 1 or end < 0: raise ValueError( 'Time relative "start" and "end" must be greater than or equal to 1' ) if group_by is None: group_by = [] q = { 'start_relative': { 'value': start, 'unit': time_unit }, 'metrics': [{ 'name': name, 'group_by': group_by }] } if end > 0: q['end_relative'] = {'value': end, 'unit': time_unit} if aggregators: q['metrics'][0]['aggregators'] = aggregators if tags: q['metrics'][0]['tags'] = tags try: response = self.__session.post(url, json=q) if response.ok: return response.json()['queries'][0] else: raise Exception( 'KairosDB Query failed: {} with status {}:{}'.format( q, response.status_code, response.text)) except requests.Timeout: raise HttpError('timeout', self.url), None, sys.exc_info()[2] except requests.ConnectionError: raise HttpError('connection failed', self.url), None, sys.exc_info()[2]