otava/grafana.py (79 lines of code) (raw):

# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. from dataclasses import asdict, dataclass from datetime import datetime from typing import List, Optional import requests from pytz import UTC from requests.exceptions import HTTPError @dataclass class GrafanaConfig: url: str user: str password: str @dataclass class GrafanaError(Exception): message: str @dataclass class Annotation: id: Optional[int] time: datetime text: str tags: List[str] class Grafana: url: str __user: str __password: str def __init__(self, grafana_conf: GrafanaConfig): self.url = grafana_conf.url self.__user = grafana_conf.user self.__password = grafana_conf.password def fetch_annotations( self, start: Optional[datetime], end: Optional[datetime], tags: List[str] = None ) -> List[Annotation]: """ Reference: - https://grafana.com/docs/grafana/latest/http_api/annotations/#find-annotations """ url = f"{self.url}api/annotations" query_parameters = {} if start is not None: query_parameters["from"] = int(start.timestamp() * 1000) if end is not None: query_parameters["to"] = int(end.timestamp() * 1000) if tags is not None: query_parameters["tags"] = tags try: response = requests.get( url=url, params=query_parameters, auth=(self.__user, self.__password) ) response.raise_for_status() json = response.json() annotations = [] for annotation_json in json: annotation = Annotation( id=annotation_json["id"], time=datetime.fromtimestamp(float(annotation_json["time"]) / 1000, tz=UTC), text=annotation_json["text"], tags=annotation_json["tags"], ) annotations.append(annotation) return annotations except KeyError as err: raise GrafanaError(f"Missing field {err.args[0]}") except HTTPError as err: raise GrafanaError(str(err)) def delete_annotations(self, *ids: int): """ Reference: - https://grafana.com/docs/grafana/latest/http_api/annotations/#delete-annotation-by-id """ url = f"{self.url}api/annotations" for annotation_id in ids: annotation_url = f"{url}/{annotation_id}" try: response = requests.delete(url=annotation_url, auth=(self.__user, self.__password)) response.raise_for_status() except HTTPError as err: raise GrafanaError(str(err)) def create_annotations(self, *annotations: Annotation): """ Reference: - https://grafana.com/docs/grafana/latest/http_api/annotations/#create-annotation """ try: url = f"{self.url}api/annotations" for annotation in annotations: data = asdict(annotation) data["time"] = int(annotation.time.timestamp() * 1000) del data["id"] response = requests.post(url=url, json=data, auth=(self.__user, self.__password)) response.raise_for_status() except HTTPError as err: raise GrafanaError(str(err))